<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Kir Shatrov feed</title>
    <link>https://kirshatrov.com/posts/</link>
    <description>Scraped feed for Kir Shatrov</description>
    <item>
      <title>Large-Scale Code Migrations with LLMs</title>
      <link>https://kirshatrov.com/posts/codebase-migrations-with-llms</link>
      <description></description>
      <guid>d9adf715a58d24dcff5d1ccf73a2c968</guid>
      <pubDate>Mon, 01 Dec 2025 00:00:00 UTC</pubDate>
      <content:encoded><![CDATA[<p>Since I joined Shopify, one of the recurring challenges has been migrating the monolith to newer APIs and DSLs. When I introduced <a href="https://github.com/shopify/job-iteration" rel="noreferrer" target="_blank">job-iteration</a>, my team had to convert most job classes from <code>def perform</code> to the Iteration API. When we upgraded from Rails 4 to Rails 5, dropping all <code>attr_accessible</code> usage took four months. Same for test DSL changes, validation frameworks, GraphQL schema patterns—any API shift at scale.</p>
<p>The pattern is always the same: you either keep the old API forever or push a herculean effort to update thousands of call sites. We still have about 1,000 spots using an input validation DSL we deprecated two years ago. Product needs to ship, so nobody prioritizes the cleanup.</p>
<h2 id="the-old-playbook">The Old Playbook</h2>
<p>In a large org, you have two options for code migrations. Staff a small team to grind through all the changes, or enforce burn-down charts for each code owner to migrate their slice. The second approach requires documentation, convincing managers of the importance, and accepting that it will take a while.</p>
<p>A tiny subset of migrations can be done with <a href="https://kirshatrov.com/posts/rewrite-code-with-rubocop" rel="noreferrer" target="_blank">static analysis</a>. But most real migrations involve context: understanding what the code does, how to preserve behavior, what the new pattern should look like in this specific case.</p>
<h2 id="what-llms-change">What LLMs Change</h2>
<p>Coding agents have shifted this landscape. You no longer have to grind through 1,000 PRs as a small group or negotiate with every team to stop using that deprecated API.</p>
<p>Define a clear set of steps, provide few-shot examples, establish the rules — and agents can do most of the work. You still review every change, but if 80% of the output is correct on the first pass, you’ve saved enormous effort.</p>
<h2 id="the-tooling-gap">The Tooling Gap</h2>
<p>Here’s the problem: you can’t ask Claude Code to “find all usage of X and migrate to Y” when there are 500 call sites. To start with, agents today lack persistent, long-lived task lists. Many startups focus on assigning GitHub issues to agents, and sure, you could create 500 issues each with a prompt — but that’s tedious and inflexible. Your v1 prompt will have 30% success rate. You’ll iterate, and v2 will hit 70%. Updating 500 issue descriptions isn’t realistic.</p>
<p>Sometimes you need to enrich the input before any work can happen. In a recent migration, I had to iterate over 300 GraphQL object definitions in Ruby. But to act on each, I needed to know which database table that object was based on — which meant matching each GraphQL type to its underlying ActiveRecord class.</p>
<p>So before the actual migration, I spawned 300 Claude instances via <code>xargs</code> just to collect that extra data. Each instance wrote its result to a file. Except multiple Claude instances calling <code>WriteFile</code> operation on the same file doesn’t work, so I had to instruct it to use <code>echo result &gt;&gt; out.csv</code> for filesystem-level appends. Then the real migration work happened—also orchestrated via <code>xargs -P</code> for parallelism.</p>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="bash"><code><span class="line"><span style="color:#6A737D"># Enrichment phase: collect metadata</span></span>
<span class="line"><span style="color:#6F42C1">cat</span><span style="color:#032F62"> graphql_types.txt</span><span style="color:#D73A49"> |</span><span style="color:#6F42C1"> xargs</span><span style="color:#005CC5"> -P</span><span style="color:#005CC5"> 20</span><span style="color:#005CC5"> -I</span><span style="color:#032F62"> {}</span><span style="color:#032F62"> bash</span><span style="color:#005CC5"> -c</span><span style="color:#005CC5"> \</span></span>
<span class="line"><span style="color:#032F62">  &#39;claude --print &#34;For GraphQL type {}, find the ActiveRecord model it wraps and echo the table name&#34; &gt;&gt; enrichment.csv&#39;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D"># Migration phase: actual changes</span></span>
<span class="line"><span style="color:#6F42C1">cat</span><span style="color:#032F62"> enrichment.csv</span><span style="color:#D73A49"> |</span><span style="color:#6F42C1"> xargs</span><span style="color:#005CC5"> -P</span><span style="color:#005CC5"> 10</span><span style="color:#005CC5"> -I</span><span style="color:#032F62"> {}</span><span style="color:#032F62"> bash</span><span style="color:#005CC5"> -c</span><span style="color:#005CC5"> \</span></span>
<span class="line"><span style="color:#032F62">  &#39;claude &#34;Migrate GraphQL type {} using enrichment data: {}&#34;&#39;</span></span></code></pre>
<p>This works, but it’s held together with shell scripts and hope.</p>
<h2 id="whats-missing">What’s Missing</h2>
<p>An orchestration layer for agent-driven migrations would need:</p>
<ol>
<li><strong>Persistent task queues</strong> — a list of 500 items that survives across sessions</li>
<li><strong>Prompt iteration</strong> — update the prompt template without recreating every task</li>
<li><strong>Enrichment pipelines</strong> — collect data in phase one, feed it to phase two</li>
<li><strong>Progress tracking</strong> — which items succeeded, which failed, which need retry with different approach</li>
</ol>
<p>None of the current agent tooling provides this. You’re left stitching together <code>xargs</code>, CSV files, and bash loops.</p>
<h2 id="the-shift">The Shift</h2>
<p>Large-scale code migrations used to require thousands of human hours and organizational coordination. Agents have changed the economics: if you can formulate the migration as clear rules with examples, you can parallelize the execution across hundreds of agent instances.</p>
<p>The tooling is primitive and you’ll write shell scripts and deal with filesystem append semantics. But the fundamental constraint has shifted from “we don’t have enough engineers” to “we haven’t built the orchestration layer yet.”</p>
<p>If you’ve built tooling for this, I’d like to hear about it.</p>]]></content:encoded>
    </item>
    <item>
      <title>Lessons in Iteration API Design</title>
      <link>https://kirshatrov.com/posts/iteration-api-design</link>
      <description></description>
      <guid>2696e2d2741024dab784463a72145043</guid>
      <pubDate>Fri, 01 Aug 2025 00:00:00 UTC</pubDate>
      <content:encoded><![CDATA[<p>When I first built <a href="https://github.com/shopify/job-iteration" rel="noreferrer" target="_blank">job-iteration</a>, the idea was elegant: let job authors return an ActiveRecord relation from <code>build_enumerable</code>, and the framework would handle the rest. Simple interface, powerful abstraction.</p>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="ruby"><code><span class="line"><span style="color:#D73A49">class</span><span style="color:#6F42C1"> MyJob</span></span>
<span class="line"><span style="color:#D73A49">  include</span><span style="color:#005CC5"> Jobs</span><span style="color:#24292E">::</span><span style="color:#005CC5">Iteration</span></span>
<span class="line"><span style="color:#24292E">  </span></span>
<span class="line"><span style="color:#D73A49">  def</span><span style="color:#6F42C1"> build_enumerable</span><span style="color:#24292E">(params)</span></span>
<span class="line"><span style="color:#005CC5">    Shop</span><span style="color:#24292E">.</span><span style="color:#6F42C1">where</span><span style="color:#24292E">(</span><span style="color:#005CC5">active:</span><span style="color:#005CC5"> true</span><span style="color:#24292E">)  </span><span style="color:#6A737D"># Just return a relation</span></span>
<span class="line"><span style="color:#D73A49">  end</span></span>
<span class="line"><span style="color:#24292E">  </span></span>
<span class="line"><span style="color:#D73A49">  def</span><span style="color:#6F42C1"> each_record</span><span style="color:#24292E">(shop, params)</span></span>
<span class="line"><span style="color:#6A737D">    # Framework handles batching, cursors, interruption</span></span>
<span class="line"><span style="color:#D73A49">  end</span></span>
<span class="line"><span style="color:#D73A49">end</span></span></code></pre>
<p>The framework would introspect the relation, figure out cursor columns, handle batching, and preserve state across interruptions. Job authors stayed blissfully unaware of the complexity.</p>
<h2 id="the-growing-beast">The Growing Beast</h2>
<p>But then reality happened. Shopify needed more than just simple relations:</p>
<ul>
<li>Sharded relations that required special database handling</li>
<li>Plain arrays for non-ActiveRecord data</li>
<li>Different cursor strategies for different column types</li>
</ul>
<p><code>Jobs::Iteration</code> grew to handle them all. By that point, it had become this:</p>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="ruby"><code><span class="line"><span style="color:#24292E">enum </span><span style="color:#005CC5">SupportedEnumerable</span><span style="color:#D73A49">&lt;</span><span style="color:#005CC5">R</span><span style="color:#D73A49">&gt;</span><span style="color:#24292E"> {</span></span>
<span class="line"><span style="color:#005CC5">  ARRelation</span><span style="color:#24292E">(</span><span style="color:#005CC5">ActiveRecord</span><span style="color:#24292E">::</span><span style="color:#005CC5">Relation</span><span style="color:#D73A49">&lt;</span><span style="color:#005CC5">R</span><span style="color:#D73A49">&gt;</span><span style="color:#24292E">)           </span><span style="color:#6A737D"># Regular AR relations</span></span>
<span class="line"><span style="color:#005CC5">  ShardedARRelation</span><span style="color:#24292E">(</span><span style="color:#005CC5">ActiveRecord</span><span style="color:#24292E">::</span><span style="color:#005CC5">Relation</span><span style="color:#D73A49">&lt;</span><span style="color:#005CC5">R</span><span style="color:#D73A49">&gt;</span><span style="color:#24292E">)    </span><span style="color:#6A737D"># Sharded AR relations  </span></span>
<span class="line"><span style="color:#005CC5">  PopEnumerable</span><span style="color:#24292E">(</span><span style="color:#005CC5">LockQueue</span><span style="color:#D73A49">&lt;</span><span style="color:#005CC5">R</span><span style="color:#D73A49">&gt;</span><span style="color:#24292E">)                     </span><span style="color:#6A737D"># Queue-based iteration</span></span>
<span class="line"><span style="color:#005CC5">  PlainEnumerable</span><span style="color:#24292E">(</span><span style="color:#005CC5">Array</span><span style="color:#D73A49">&lt;</span><span style="color:#005CC5">R</span><span style="color:#24292E"> where </span><span style="color:#005CC5">R:</span><span style="color:#005CC5"> NotActiveRecord</span><span style="color:#D73A49">&gt;</span><span style="color:#24292E">) </span><span style="color:#6A737D"># Plain arrays</span></span>
<span class="line"><span style="color:#6A737D">  # Plus some other variant I&#39;m missing</span></span>
<span class="line"><span style="color:#24292E">}</span></span></code></pre>
<p>And the actual branching logic that handled them:</p>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="ruby"><code><span class="line"><span style="color:#D73A49">if</span><span style="color:#24292E"> collection.</span><span style="color:#6F42C1">is_a?</span><span style="color:#24292E">(</span><span style="color:#005CC5">ActiveRecord</span><span style="color:#24292E">::</span><span style="color:#005CC5">Relation</span><span style="color:#24292E">)</span></span>
<span class="line"><span style="color:#E36209">  cursor_columns</span><span style="color:#D73A49"> =</span><span style="color:#005CC5"> Array</span><span style="color:#24292E">.</span><span style="color:#6F42C1">wrap</span><span style="color:#24292E">(iteration_options[</span><span style="color:#005CC5">:columns</span><span style="color:#24292E">] </span><span style="color:#D73A49">||</span><span style="color:#032F62"> &#34;</span><span style="color:#032F62">#{collection.</span><span style="color:#6F42C1">table_name</span><span style="color:#032F62">}</span><span style="color:#032F62">.</span><span style="color:#032F62">#{collection.</span><span style="color:#6F42C1">primary_key</span><span style="color:#032F62">}</span><span style="color:#032F62">&#34;</span><span style="color:#24292E">)</span></span>
<span class="line"><span style="color:#E36209">  options</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> iteration_options.</span><span style="color:#6F42C1">merge</span><span style="color:#24292E">(</span></span>
<span class="line"><span style="color:#005CC5">    columns:</span><span style="color:#24292E"> cursor_columns,</span></span>
<span class="line"><span style="color:#005CC5">    start:</span><span style="color:#24292E"> cursor_position,</span></span>
<span class="line"><span style="color:#005CC5">    batch_size:</span><span style="color:#24292E"> batch_size,</span></span>
<span class="line"><span style="color:#24292E">  )</span></span>
<span class="line"><span style="color:#24292E">  </span></span>
<span class="line"><span style="color:#6F42C1">  find_in_batches</span><span style="color:#24292E">(collection, options) </span><span style="color:#D73A49">do</span><span style="color:#24292E"> |records|</span></span>
<span class="line"><span style="color:#6A737D">    # Different logic for each_record vs each_batch</span></span>
<span class="line"><span style="color:#6A737D">    # Different cursor position tracking</span></span>
<span class="line"><span style="color:#D73A49">  end</span></span>
<span class="line"><span style="color:#D73A49">elsif</span><span style="color:#6F42C1"> collection_is_a?</span><span style="color:#24292E">(</span><span style="color:#005CC5">Array</span><span style="color:#24292E">)</span></span>
<span class="line"><span style="color:#6A737D">  # Handle plain Array</span></span>
<span class="line"><span style="color:#D73A49">elsif</span><span style="color:#6F42C1"> collection_is_a?</span><span style="color:#24292E">(</span><span style="color:#005CC5">CSV</span><span style="color:#24292E">)</span></span>
<span class="line"><span style="color:#6A737D">  # Completely different path for arrays, queues, etc.</span></span>
<span class="line"><span style="color:#D73A49">elsif</span><span style="color:#24292E"> ...</span></span>
<span class="line"><span style="color:#D73A49">end</span></span></code></pre>
<h2 id="guillaumes-challenge">Guillaume’s Challenge</h2>
<p>In August 2017, my colleague Guillaume looked at this and said: “I’m not smart enough to understand <code>Jobs::Iteration</code>.”</p>
<p>But instead of walking away, he made a provocative argument. What if the complexity wasn’t inevitable? What if <code>Jobs::Iteration</code> was trying to be too smart?</p>
<p>His insight was about types and interfaces. Instead of:</p>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="ruby"><code><span class="line"><span style="color:#6A737D"># Jobs::Iteration knowing about everything</span></span>
<span class="line"><span style="color:#D73A49">if</span><span style="color:#24292E"> collection.</span><span style="color:#6F42C1">is_a?</span><span style="color:#24292E">(</span><span style="color:#005CC5">ActiveRecord</span><span style="color:#24292E">::</span><span style="color:#005CC5">Relation</span><span style="color:#24292E">)</span></span>
<span class="line"><span style="color:#6A737D">  # complex cursor logic</span></span>
<span class="line"><span style="color:#6A737D">  # sharing logic  </span></span>
<span class="line"><span style="color:#6A737D">  # batching logic</span></span>
<span class="line"><span style="color:#D73A49">elsif</span><span style="color:#24292E"> collection.</span><span style="color:#6F42C1">is_a?</span><span style="color:#24292E">(</span><span style="color:#005CC5">Array</span><span style="color:#24292E">)</span></span>
<span class="line"><span style="color:#6A737D">  # different logic</span></span>
<span class="line"><span style="color:#D73A49">end</span></span></code></pre>
<p>Move to:</p>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="ruby"><code><span class="line"><span style="color:#6A737D"># Jobs::Iteration knowing about nothing</span></span>
<span class="line"><span style="color:#24292E">enumerable.</span><span style="color:#6F42C1">records</span><span style="color:#24292E">.</span><span style="color:#6F42C1">each</span><span style="color:#D73A49"> do</span><span style="color:#24292E"> |record, cursor|</span></span>
<span class="line"><span style="color:#6A737D">  # just consume tuples</span></span>
<span class="line"><span style="color:#D73A49">end</span></span></code></pre>
<h2 id="the-resistance-point">The Resistance Point</h2>
<p>My natural resistance was: “Why make job authors deal with enumerators instead of just passing relations?”</p>
<p>Guillaume’s answer was essentially about cognitive load distribution. Yes, individual jobs become slightly more complex, but the system’s total complexity decreases because:</p>
<ol>
<li>Each enumeration strategy is isolated</li>
<li>The strategies become independently testable</li>
<li>The core iteration logic becomes trivial</li>
<li>New enumeration types don’t require touching the core</li>
</ol>
<h2 id="the-conclusion">The Conclusion</h2>
<p>The decision taught me something counterintuitive: sometimes more verbose code is better code.</p>
<p>Instead of one magical method that “just works”:</p>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="ruby"><code><span class="line"><span style="color:#D73A49">def</span><span style="color:#6F42C1"> build_enumerable</span><span style="color:#24292E">(params)</span></span>
<span class="line"><span style="color:#005CC5">  Shop</span><span style="color:#24292E">.</span><span style="color:#6F42C1">where</span><span style="color:#24292E">(</span><span style="color:#005CC5">active:</span><span style="color:#005CC5"> true</span><span style="color:#24292E">)</span></span>
<span class="line"><span style="color:#D73A49">end</span></span></code></pre>
<p>Job authors would now write:</p>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="ruby"><code><span class="line"><span style="color:#D73A49">def</span><span style="color:#6F42C1"> build_enumerable</span><span style="color:#24292E">(params, </span><span style="color:#005CC5">cursor:</span><span style="color:#24292E">)</span></span>
<span class="line"><span style="color:#24292E">  enumerator_builder.</span><span style="color:#6F42C1">active_record_on_records</span><span style="color:#24292E">(</span></span>
<span class="line"><span style="color:#005CC5">    Shop</span><span style="color:#24292E">.</span><span style="color:#6F42C1">where</span><span style="color:#24292E">(</span><span style="color:#005CC5">active:</span><span style="color:#005CC5"> true</span><span style="color:#24292E">),</span></span>
<span class="line"><span style="color:#005CC5">    cursor:</span><span style="color:#24292E"> cursor</span></span>
<span class="line"><span style="color:#24292E">  )</span></span>
<span class="line"><span style="color:#D73A49">end</span></span></code></pre>
<p>More lines? Yes. More explicit dependencies? Absolutely. But also more honest about what’s actually happening, more testable, and paradoxically simpler because each piece does one thing well.</p>
<p>It was about admitting when elegant abstraction becomes cognitive overload, and choosing explicitness over magic - even when the magic was convenient.</p>]]></content:encoded>
    </item>
    <item>
      <title>Sharing memory between processes: learnings from Postgres</title>
      <link>https://kirshatrov.com/posts/pg-shmem</link>
      <description></description>
      <guid>07fee6365f0b105710004c9e337b8361</guid>
      <pubDate>Fri, 01 Aug 2025 00:00:00 UTC</pubDate>
      <content:encoded><![CDATA[<p>This post summarizes some Claude-assisted learnings from how PostgreSQL shares memory between its workers that are essentially forked processes.</p>
<ul>
<li>Two-layer design: Static shared memory for core structures + Dynamic shared memory for parallel operations</li>
</ul>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="c"><code><span class="line"><span style="color:#6A737D">// Static allocation for core structures</span></span>
<span class="line"><span style="color:#D73A49">void</span><span style="color:#D73A49"> *</span><span style="color:#24292E">buffer_descriptors </span><span style="color:#D73A49">=</span><span style="color:#6F42C1"> ShmemInitStruct</span><span style="color:#24292E">(</span><span style="color:#032F62">&#34;Buffer Descriptors&#34;</span><span style="color:#24292E">, size, </span><span style="color:#D73A49">&amp;</span><span style="color:#E36209">found</span><span style="color:#24292E">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D">// Dynamic allocation for parallel workers</span></span>
<span class="line"><span style="color:#24292E">dsm_segment </span><span style="color:#D73A49">*</span><span style="color:#24292E">seg </span><span style="color:#D73A49">=</span><span style="color:#6F42C1"> dsm_create</span><span style="color:#24292E">(</span><span style="color:#005CC5">1024</span><span style="color:#D73A49"> *</span><span style="color:#005CC5"> 1024</span><span style="color:#24292E">, </span><span style="color:#005CC5">0</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#D73A49">void</span><span style="color:#D73A49"> *</span><span style="color:#24292E">parallel_data </span><span style="color:#D73A49">=</span><span style="color:#6F42C1"> dsm_segment_address</span><span style="color:#24292E">(seg);</span></span></code></pre>
<ul>
<li>Single segment inheritance: Postmaster creates segment at fixed address, backends inherit correct pointers via fork()</li>
</ul>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="c"><code><span class="line"><span style="color:#6A737D">// In postmaster: create at consistent virtual address</span></span>
<span class="line"><span style="color:#24292E">ShmemBase </span><span style="color:#D73A49">=</span><span style="color:#6F42C1"> PGSharedMemoryCreate</span><span style="color:#24292E">(total_size, </span><span style="color:#D73A49">&amp;</span><span style="color:#E36209">shim</span><span style="color:#24292E">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D">// In backend: inherit same virtual address automatically</span></span>
<span class="line"><span style="color:#6A737D">// No remapping needed - pointers work across processes</span></span></code></pre>
<ul>
<li>Name-based lookup: Hash table maps structure names to memory locations (<a href="https://github.com/postgres/postgres/blob/master/src/backend/storage/ipc/shmem.c#L91" rel="noreferrer" target="_blank">https://github.com/postgres/postgres/blob/master/src/backend/storage/ipc/shmem.c#L91</a>)</li>
</ul>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="c"><code><span class="line"><span style="color:#6A737D">// Register named structure in shared memory index</span></span>
<span class="line"><span style="color:#D73A49">void</span><span style="color:#D73A49"> *</span><span style="color:#24292E">my_data </span><span style="color:#D73A49">=</span><span style="color:#6F42C1"> ShmemInitStruct</span><span style="color:#24292E">(</span><span style="color:#032F62">&#34;Buffer Strategy Status&#34;</span><span style="color:#24292E">, </span><span style="color:#D73A49">sizeof</span><span style="color:#24292E">(BufferStrategyControl), </span><span style="color:#D73A49">&amp;</span><span style="color:#E36209">found</span><span style="color:#24292E">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D">// Later processes find by name</span></span>
<span class="line"><span style="color:#24292E">result </span><span style="color:#D73A49">=</span><span style="color:#6F42C1"> hash_search</span><span style="color:#24292E">(ShmemIndex, </span><span style="color:#032F62">&#34;Buffer Strategy Status&#34;</span><span style="color:#24292E">, HASH_FIND, </span><span style="color:#005CC5">NULL</span><span style="color:#24292E">);</span></span></code></pre>
<ul>
<li>No deallocation: Memory never freed, hash buckets reused when items deleted</li>
<li>Cache-line alignment: All allocations aligned to 64-byte boundaries to prevent false sharing (<a href="https://github.com/postgres/postgres/blob/master/src/backend/storage/ipc/shmem.c#L203" rel="noreferrer" target="_blank">https://github.com/postgres/postgres/blob/master/src/backend/storage/ipc/shmem.c#L203</a>)</li>
</ul>
<h2 id="anti-thundering-herd-mechanisms">Anti-Thundering Herd Mechanisms</h2>
<ul>
<li>Exclusive locking with atomic check-and-allocate (<a href="https://github.com/postgres/postgres/blob/master/src/backend/storage/ipc/shmem.c#L392-L429" rel="noreferrer" target="_blank">https://github.com/postgres/postgres/blob/master/src/backend/storage/ipc/shmem.c#L392-L429</a>):</li>
</ul>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="c"><code><span class="line"><span style="color:#6F42C1">LWLockAcquire</span><span style="color:#24292E">(ShmemIndexLock, LW_EXCLUSIVE);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D">// Atomic: either find existing or create new entry (never both)</span></span>
<span class="line"><span style="color:#24292E">result </span><span style="color:#D73A49">=</span><span style="color:#6F42C1"> hash_search</span><span style="color:#24292E">(ShmemIndex, </span><span style="color:#032F62">&#34;MyExpensiveData&#34;</span><span style="color:#24292E">, HASH_ENTER_NULL, </span><span style="color:#D73A49">&amp;</span><span style="color:#E36209">found</span><span style="color:#24292E">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49">if</span><span style="color:#24292E"> (found) {</span></span>
<span class="line"><span style="color:#6A737D">    // Someone else already computed it</span></span>
<span class="line"><span style="color:#D73A49">    return</span><span style="color:#24292E"> result-&gt;location;</span><span style="color:#6A737D">  // Just return existing pointer</span></span>
<span class="line"><span style="color:#24292E">} </span><span style="color:#D73A49">else</span><span style="color:#24292E"> {</span></span>
<span class="line"><span style="color:#6A737D">    // We won the race - allocate and initialize</span></span>
<span class="line"><span style="color:#24292E">    ptr </span><span style="color:#D73A49">=</span><span style="color:#6F42C1"> ShmemAllocRaw</span><span style="color:#24292E">(size, </span><span style="color:#D73A49">&amp;</span><span style="color:#24292E">allocated_size);</span></span>
<span class="line"><span style="color:#24292E">    result-&gt;location </span><span style="color:#D73A49">=</span><span style="color:#24292E"> ptr;</span></span>
<span class="line"><span style="color:#6A737D">    // Now do expensive initialization work</span></span>
<span class="line"><span style="color:#24292E">}</span></span>
<span class="line"><span style="color:#6F42C1">LWLockRelease</span><span style="color:#24292E">(ShmemIndexLock);</span></span></code></pre>
<ul>
<li>Condition variables for efficient waiting (<a href="https://github.com/postgres/postgres/blob/master/src/backend/storage/lmgr/condition_variable.c" rel="noreferrer" target="_blank">https://github.com/postgres/postgres/blob/master/src/backend/storage/lmgr/condition_variable.c</a>):</li>
</ul>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="c"><code><span class="line"><span style="color:#6A737D">// Waiter pattern</span></span>
<span class="line"><span style="color:#6F42C1">ConditionVariablePrepareToSleep</span><span style="color:#24292E">(</span><span style="color:#D73A49">&amp;</span><span style="color:#24292E">slot</span><span style="color:#D73A49">-&gt;</span><span style="color:#E36209">active_cv</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#D73A49">while</span><span style="color:#24292E"> (slot</span><span style="color:#D73A49">-&gt;</span><span style="color:#24292E">active_pid </span><span style="color:#D73A49">!=</span><span style="color:#005CC5"> 0</span><span style="color:#24292E">)</span><span style="color:#6A737D">  // While someone else owns the slot</span></span>
<span class="line"><span style="color:#6F42C1">    ConditionVariableSleep</span><span style="color:#24292E">(</span><span style="color:#D73A49">&amp;</span><span style="color:#24292E">slot</span><span style="color:#D73A49">-&gt;</span><span style="color:#E36209">active_cv</span><span style="color:#24292E">, WAIT_EVENT_REPLICATION_SLOT_DROP);</span></span>
<span class="line"><span style="color:#6F42C1">ConditionVariableCancelSleep</span><span style="color:#24292E">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D">// Signaler pattern</span></span>
<span class="line"><span style="color:#24292E">slot</span><span style="color:#D73A49">-&gt;</span><span style="color:#24292E">active_pid </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> 0</span><span style="color:#24292E">;</span><span style="color:#6A737D">  // Release the resource</span></span>
<span class="line"><span style="color:#6F42C1">ConditionVariableBroadcast</span><span style="color:#24292E">(</span><span style="color:#D73A49">&amp;</span><span style="color:#24292E">slot</span><span style="color:#D73A49">-&gt;</span><span style="color:#E36209">active_cv</span><span style="color:#24292E">);</span><span style="color:#6A737D">  // Wake all waiters</span></span></code></pre>
<ul>
<li>Lock-free message queues: Atomic counters + memory barriers (<a href="https://github.com/postgres/postgres/blob/master/src/backend/storage/ipc/shm_mq.c#L76-L77" rel="noreferrer" target="_blank">https://github.com/postgres/postgres/blob/master/src/backend/storage/ipc/shm_mq.c#L76-L77</a>)</li>
</ul>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="c"><code><span class="line"><span style="color:#6A737D">// Producer atomically updates write position</span></span>
<span class="line"><span style="color:#6F42C1">pg_atomic_write_u64</span><span style="color:#24292E">(</span><span style="color:#D73A49">&amp;</span><span style="color:#24292E">mq</span><span style="color:#D73A49">-&gt;</span><span style="color:#E36209">mq_bytes_written</span><span style="color:#24292E">, new_write_pos);</span></span>
<span class="line"><span style="color:#6F42C1">pg_write_barrier</span><span style="color:#24292E">();</span><span style="color:#6A737D">  // Ensure data visible before updating counter</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D">// Consumer atomically reads without locks</span></span>
<span class="line"><span style="color:#24292E">uint64 bytes_written </span><span style="color:#D73A49">=</span><span style="color:#6F42C1"> pg_atomic_read_u64</span><span style="color:#24292E">(</span><span style="color:#D73A49">&amp;</span><span style="color:#24292E">mq</span><span style="color:#D73A49">-&gt;</span><span style="color:#E36209">mq_bytes_written</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#24292E">uint64 bytes_available </span><span style="color:#D73A49">=</span><span style="color:#24292E"> bytes_written </span><span style="color:#D73A49">-</span><span style="color:#24292E"> mq_bytes_read;</span></span></code></pre>
<h2 id="ttl-implementation-patterns">TTL Implementation Patterns</h2>
<ul>
<li>Expiry check with immediate invalidation:</li>
</ul>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="c"><code><span class="line"><span style="color:#D73A49">bool</span><span style="color:#6F42C1"> is_expired</span><span style="color:#24292E">(CacheEntry </span><span style="color:#D73A49">*</span><span style="color:#E36209">entry</span><span style="color:#24292E">) {</span></span>
<span class="line"><span style="color:#D73A49">    if</span><span style="color:#24292E"> (</span><span style="color:#D73A49">!</span><span style="color:#24292E">entry-&gt;initialized) </span><span style="color:#D73A49">return</span><span style="color:#005CC5"> false</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#D73A49">    return</span><span style="color:#24292E"> (</span><span style="color:#6F42C1">time</span><span style="color:#24292E">(</span><span style="color:#005CC5">NULL</span><span style="color:#24292E">) </span><span style="color:#D73A49">-</span><span style="color:#24292E"> entry-&gt;created_at) </span><span style="color:#D73A49">&gt;</span><span style="color:#24292E"> entry-&gt;ttl_seconds;</span></span>
<span class="line"><span style="color:#24292E">}</span></span></code></pre>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="c"><code><span class="line"><span style="color:#6A737D">// In get operation</span></span>
<span class="line"><span style="color:#6F42C1">pthread_mutex_lock</span><span style="color:#24292E">(</span><span style="color:#D73A49">&amp;</span><span style="color:#24292E">entry</span><span style="color:#D73A49">-&gt;</span><span style="color:#E36209">mutex</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#D73A49">if</span><span style="color:#24292E"> (entry</span><span style="color:#D73A49">-&gt;</span><span style="color:#24292E">initialized </span><span style="color:#D73A49">&amp;&amp;</span><span style="color:#6F42C1"> is_expired</span><span style="color:#24292E">(entry)) {</span></span>
<span class="line"><span style="color:#6F42C1">    printf</span><span style="color:#24292E">(</span><span style="color:#032F62">&#34;Cache expired (age: </span><span style="color:#005CC5">%ld</span><span style="color:#032F62">s &gt; </span><span style="color:#005CC5">%ld</span><span style="color:#032F62">s TTL)</span><span style="color:#005CC5">\n</span><span style="color:#032F62">&#34;</span><span style="color:#24292E">,</span></span>
<span class="line"><span style="color:#6F42C1">            time</span><span style="color:#24292E">(</span><span style="color:#005CC5">NULL</span><span style="color:#24292E">) </span><span style="color:#D73A49">-</span><span style="color:#24292E"> entry-&gt;created_at, entry-&gt;ttl_seconds);</span></span>
<span class="line"><span style="color:#24292E">    entry-&gt;initialized </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> false</span><span style="color:#24292E">;</span><span style="color:#6A737D">  // Treat as cache miss</span></span>
<span class="line"><span style="color:#24292E">}</span></span></code></pre>
<ul>
<li>Per-key TTL with timestamp tracking:</li>
</ul>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="c"><code><span class="line"><span style="color:#6A737D">// When storing result</span></span>
<span class="line"><span style="color:#24292E">entry</span><span style="color:#D73A49">-&gt;</span><span style="color:#24292E">value </span><span style="color:#D73A49">=</span><span style="color:#24292E"> computed_result;</span></span>
<span class="line"><span style="color:#24292E">entry</span><span style="color:#D73A49">-&gt;</span><span style="color:#24292E">created_at </span><span style="color:#D73A49">=</span><span style="color:#6F42C1"> time</span><span style="color:#24292E">(</span><span style="color:#005CC5">NULL</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#24292E">entry</span><span style="color:#D73A49">-&gt;</span><span style="color:#24292E">ttl_seconds </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> 300</span><span style="color:#24292E">;</span><span style="color:#6A737D">  // 5 minute TTL</span></span>
<span class="line"><span style="color:#24292E">entry</span><span style="color:#D73A49">-&gt;</span><span style="color:#24292E">initialized </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> true</span><span style="color:#24292E">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D">// When checking freshness</span></span>
<span class="line"><span style="color:#D73A49">time_t</span><span style="color:#24292E"> age </span><span style="color:#D73A49">=</span><span style="color:#6F42C1"> time</span><span style="color:#24292E">(</span><span style="color:#005CC5">NULL</span><span style="color:#24292E">) </span><span style="color:#D73A49">-</span><span style="color:#24292E"> entry</span><span style="color:#D73A49">-&gt;</span><span style="color:#24292E">created_at;</span></span>
<span class="line"><span style="color:#D73A49">bool</span><span style="color:#24292E"> fresh </span><span style="color:#D73A49">=</span><span style="color:#24292E"> age </span><span style="color:#D73A49">&lt;=</span><span style="color:#24292E"> entry</span><span style="color:#D73A49">-&gt;</span><span style="color:#24292E">ttl_seconds;</span></span></code></pre>
<ul>
<li>Combined with thundering herd protection:</li>
</ul>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="c"><code><span class="line"><span style="color:#D73A49">if</span><span style="color:#24292E"> (entry</span><span style="color:#D73A49">-&gt;</span><span style="color:#24292E">initialized </span><span style="color:#D73A49">&amp;&amp;</span><span style="color:#D73A49"> !</span><span style="color:#6F42C1">is_expired</span><span style="color:#24292E">(entry)) {</span></span>
<span class="line"><span style="color:#D73A49">    return</span><span style="color:#24292E"> entry-&gt;value;</span><span style="color:#6A737D">  // Fresh cache hit</span></span>
<span class="line"><span style="color:#24292E">}</span></span>
<span class="line"><span style="color:#D73A49">if</span><span style="color:#24292E"> (entry</span><span style="color:#D73A49">-&gt;</span><span style="color:#24292E">initializing) {</span></span>
<span class="line"><span style="color:#6F42C1">    wait_for_computation</span><span style="color:#24292E">();</span><span style="color:#6A737D">  // Someone else recomputing expired data</span></span>
<span class="line"><span style="color:#24292E">}</span></span>
<span class="line"><span style="color:#6A737D">// Expired + not computing = we recompute</span></span></code></pre>
<h2 id="safety-and-synchronization">Safety and Synchronization</h2>
<ul>
<li>Magic number validation (<a href="https://github.com/postgres/postgres/blob/master/src/include/storage/pg_shmem.h#L32" rel="noreferrer" target="_blank">https://github.com/postgres/postgres/blob/master/src/include/storage/pg_shmem.h#L32</a>):</li>
</ul>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="c"><code><span class="line"><span style="color:#D73A49">typedef</span><span style="color:#D73A49"> struct</span><span style="color:#24292E"> PGShmemHeader {</span></span>
<span class="line"><span style="color:#24292E">    int32 magic;</span><span style="color:#6A737D">  // #define PGShmemMagic 679834894</span></span>
<span class="line"><span style="color:#D73A49">    pid_t</span><span style="color:#24292E"> creatorPID;</span></span>
<span class="line"><span style="color:#005CC5">    Size</span><span style="color:#24292E"> totalsize;</span></span>
<span class="line"><span style="color:#6A737D">    // ...</span></span>
<span class="line"><span style="color:#24292E">} PGShmemHeader;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D">// Validation on attach</span></span>
<span class="line"><span style="color:#D73A49">if</span><span style="color:#24292E"> (header</span><span style="color:#D73A49">-&gt;</span><span style="color:#24292E">magic </span><span style="color:#D73A49">!=</span><span style="color:#24292E"> PGShmemMagic)</span></span>
<span class="line"><span style="color:#6F42C1">    elog</span><span style="color:#24292E">(FATAL, </span><span style="color:#032F62">&#34;wrong shared memory segment&#34;</span><span style="color:#24292E">);</span></span></code></pre>
<ul>
<li>Process validation with creator PID:</li>
</ul>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="plaintext"><code><span class="line"><span>// Store creator info</span></span>
<span class="line"><span>header-&gt;creatorPID = getpid();</span></span>
<span class="line"><span>header-&gt;device = stat_buf.st_dev;     // Prevent cross-instance sharing</span></span>
<span class="line"><span>header-&gt;inode = stat_buf.st_ino;</span></span>
<span class="line"><span></span></span>
<span class="line"><span>// Validate on attach</span></span>
<span class="line"><span>if (header-&gt;device != our_device || header-&gt;inode != our_inode)</span></span>
<span class="line"><span>    elog(FATAL, &#34;shared memory from different data directory&#34;);</span></span></code></pre>
<ul>
<li>Resource cleanup with exit callbacks (<a href="https://github.com/postgres/postgres/blob/master/src/backend/storage/ipc/ipc.c#L79-L85" rel="noreferrer" target="_blank">https://github.com/postgres/postgres/blob/master/src/backend/storage/ipc/ipc.c#L79-L85</a>):</li>
</ul>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="c"><code><span class="line"><span style="color:#6A737D">// Register cleanup function</span></span>
<span class="line"><span style="color:#6F42C1">on_shmem_exit</span><span style="color:#24292E">(cleanup_my_resources, </span><span style="color:#6F42C1">PointerGetDatum</span><span style="color:#24292E">(my_data));</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D">// Automatic cleanup on process exit</span></span>
<span class="line"><span style="color:#D73A49">static</span><span style="color:#D73A49"> void</span><span style="color:#6F42C1"> cleanup_my_resources</span><span style="color:#24292E">(</span><span style="color:#D73A49">int</span><span style="color:#E36209"> code</span><span style="color:#24292E">, Datum </span><span style="color:#E36209">arg</span><span style="color:#24292E">) {</span></span>
<span class="line"><span style="color:#24292E">    MyStruct </span><span style="color:#D73A49">*</span><span style="color:#24292E">data </span><span style="color:#D73A49">=</span><span style="color:#24292E"> (MyStruct</span><span style="color:#D73A49">*</span><span style="color:#24292E">) </span><span style="color:#6F42C1">DatumGetPointer</span><span style="color:#24292E">(arg);</span></span>
<span class="line"><span style="color:#24292E">    data-&gt;active_pid </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> 0</span><span style="color:#24292E">;</span><span style="color:#6A737D">  // Release ownership</span></span>
<span class="line"><span style="color:#6F42C1">    ConditionVariableBroadcast</span><span style="color:#24292E">(</span><span style="color:#D73A49">&amp;</span><span style="color:#24292E">data-&gt;cv);</span><span style="color:#6A737D">  // Wake waiters</span></span>
<span class="line"><span style="color:#24292E">}</span></span></code></pre>
<ul>
<li>Spinlock protection for short critical sections:</li>
</ul>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="c"><code><span class="line"><span style="color:#6F42C1">SpinLockAcquire</span><span style="color:#24292E">(</span><span style="color:#D73A49">&amp;</span><span style="color:#24292E">entry</span><span style="color:#D73A49">-&gt;</span><span style="color:#E36209">mutex</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#24292E">entry</span><span style="color:#D73A49">-&gt;</span><span style="color:#24292E">reference_count</span><span style="color:#D73A49">++</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#24292E">entry</span><span style="color:#D73A49">-&gt;</span><span style="color:#24292E">last_accessed </span><span style="color:#D73A49">=</span><span style="color:#6F42C1"> GetCurrentTimestamp</span><span style="color:#24292E">();</span></span>
<span class="line"><span style="color:#6F42C1">SpinLockRelease</span><span style="color:#24292E">(</span><span style="color:#D73A49">&amp;</span><span style="color:#24292E">entry</span><span style="color:#D73A49">-&gt;</span><span style="color:#E36209">mutex</span><span style="color:#24292E">);</span></span></code></pre>
<h2 id="coordination-patterns">Coordination Patterns</h2>
<ul>
<li>Complete initialization race pattern:</li>
</ul>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="c"><code><span class="line"><span style="color:#6F42C1">pthread_mutex_lock</span><span style="color:#24292E">(</span><span style="color:#D73A49">&amp;</span><span style="color:#24292E">entry</span><span style="color:#D73A49">-&gt;</span><span style="color:#E36209">mutex</span><span style="color:#24292E">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49">if</span><span style="color:#24292E"> (entry</span><span style="color:#D73A49">-&gt;</span><span style="color:#24292E">initialized </span><span style="color:#D73A49">&amp;&amp;</span><span style="color:#D73A49"> !</span><span style="color:#6F42C1">is_expired</span><span style="color:#24292E">(entry)) {</span></span>
<span class="line"><span style="color:#24292E">    result </span><span style="color:#D73A49">=</span><span style="color:#24292E"> entry-&gt;value;</span><span style="color:#6A737D">  // Cache hit</span></span>
<span class="line"><span style="color:#6F42C1">    pthread_mutex_unlock</span><span style="color:#24292E">(</span><span style="color:#D73A49">&amp;</span><span style="color:#24292E">entry-&gt;mutex);</span></span>
<span class="line"><span style="color:#D73A49">    return</span><span style="color:#24292E"> result;</span></span>
<span class="line"><span style="color:#24292E">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49">if</span><span style="color:#24292E"> (entry</span><span style="color:#D73A49">-&gt;</span><span style="color:#24292E">initializing) {</span></span>
<span class="line"><span style="color:#6A737D">    // Another process computing</span></span>
<span class="line"><span style="color:#D73A49">    while</span><span style="color:#24292E"> (</span><span style="color:#D73A49">!</span><span style="color:#24292E">entry-&gt;initialized)</span></span>
<span class="line"><span style="color:#6F42C1">        pthread_cond_wait</span><span style="color:#24292E">(</span><span style="color:#D73A49">&amp;</span><span style="color:#24292E">entry-&gt;init_cv, </span><span style="color:#D73A49">&amp;</span><span style="color:#24292E">entry-&gt;mutex);</span></span>
<span class="line"><span style="color:#24292E">    result </span><span style="color:#D73A49">=</span><span style="color:#24292E"> entry-&gt;value;</span></span>
<span class="line"><span style="color:#6F42C1">    pthread_mutex_unlock</span><span style="color:#24292E">(</span><span style="color:#D73A49">&amp;</span><span style="color:#24292E">entry-&gt;mutex);</span></span>
<span class="line"><span style="color:#D73A49">    return</span><span style="color:#24292E"> result;</span></span>
<span class="line"><span style="color:#24292E">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D">// We won the race</span></span>
<span class="line"><span style="color:#24292E">entry</span><span style="color:#D73A49">-&gt;</span><span style="color:#24292E">initializing </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> true</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#6F42C1">pthread_mutex_unlock</span><span style="color:#24292E">(</span><span style="color:#D73A49">&amp;</span><span style="color:#24292E">entry</span><span style="color:#D73A49">-&gt;</span><span style="color:#E36209">mutex</span><span style="color:#24292E">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D">// Expensive work OUTSIDE the lock</span></span>
<span class="line"><span style="color:#24292E">expensive_result </span><span style="color:#D73A49">=</span><span style="color:#6F42C1"> compute_expensive_thing</span><span style="color:#24292E">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D">// Store result and notify</span></span>
<span class="line"><span style="color:#6F42C1">pthread_mutex_lock</span><span style="color:#24292E">(</span><span style="color:#D73A49">&amp;</span><span style="color:#24292E">entry</span><span style="color:#D73A49">-&gt;</span><span style="color:#E36209">mutex</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#24292E">entry</span><span style="color:#D73A49">-&gt;</span><span style="color:#24292E">value </span><span style="color:#D73A49">=</span><span style="color:#24292E"> expensive_result;</span></span>
<span class="line"><span style="color:#24292E">entry</span><span style="color:#D73A49">-&gt;</span><span style="color:#24292E">created_at </span><span style="color:#D73A49">=</span><span style="color:#6F42C1"> time</span><span style="color:#24292E">(</span><span style="color:#005CC5">NULL</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#24292E">entry</span><span style="color:#D73A49">-&gt;</span><span style="color:#24292E">initialized </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> true</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#24292E">entry</span><span style="color:#D73A49">-&gt;</span><span style="color:#24292E">initializing </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> false</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#6F42C1">pthread_mutex_unlock</span><span style="color:#24292E">(</span><span style="color:#D73A49">&amp;</span><span style="color:#24292E">entry</span><span style="color:#D73A49">-&gt;</span><span style="color:#E36209">mutex</span><span style="color:#24292E">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6F42C1">pthread_cond_broadcast</span><span style="color:#24292E">(</span><span style="color:#D73A49">&amp;</span><span style="color:#24292E">entry</span><span style="color:#D73A49">-&gt;</span><span style="color:#E36209">init_cv</span><span style="color:#24292E">);</span><span style="color:#6A737D">  // Wake all waiters</span></span></code></pre>
<ul>
<li>
<p>State transitions: uninitialized → initializing → initialized → expired → uninitialized</p>
</li>
<li>
<p>Retry with goto pattern (PostgreSQL style):</p>
</li>
</ul>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="c"><code><span class="line"><span style="color:#24292E">retry:</span></span>
<span class="line"><span style="color:#6F42C1">LWLockAcquire</span><span style="color:#24292E">(ReplicationOriginLock, LW_EXCLUSIVE);</span></span>
<span class="line"><span style="color:#D73A49">if</span><span style="color:#24292E"> (origin</span><span style="color:#D73A49">-&gt;</span><span style="color:#24292E">acquired_by </span><span style="color:#D73A49">!=</span><span style="color:#005CC5"> 0</span><span style="color:#24292E">) {</span></span>
<span class="line"><span style="color:#24292E">    cv </span><span style="color:#D73A49">=</span><span style="color:#D73A49"> &amp;</span><span style="color:#24292E">origin-&gt;origin_cv;</span></span>
<span class="line"><span style="color:#6F42C1">    LWLockRelease</span><span style="color:#24292E">(ReplicationOriginLock);</span></span>
<span class="line"><span style="color:#6F42C1">    ConditionVariableSleep</span><span style="color:#24292E">(cv, WAIT_EVENT_REPLICATION_ORIGIN_DROP);</span></span>
<span class="line"><span style="color:#D73A49">    goto</span><span style="color:#24292E"> retry;</span><span style="color:#6A737D">  // Start over</span></span>
<span class="line"><span style="color:#24292E">}</span></span>
<span class="line"><span style="color:#6A737D">// Proceed with acquisition</span></span></code></pre>
<h2 id="performance-optimizations">Performance Optimizations</h2>
<ul>
<li>NUMA-aware allocation: Track which memory nodes hold data for optimal CPU affinity</li>
<li>Huge pages: Use 2MB/1GB pages instead of 4KB to reduce TLB pressure</li>
<li>Computation outside locks: Always release locks before expensive operations:</li>
</ul>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="c"><code><span class="line"><span style="color:#6A737D">// BAD: holding lock during expensive work</span></span>
<span class="line"><span style="color:#6F42C1">pthread_mutex_lock</span><span style="color:#24292E">(</span><span style="color:#D73A49">&amp;</span><span style="color:#E36209">mutex</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#24292E">expensive_result </span><span style="color:#D73A49">=</span><span style="color:#6F42C1"> heavy_computation</span><span style="color:#24292E">();</span><span style="color:#6A737D">  // Blocks everyone</span></span>
<span class="line"><span style="color:#24292E">data</span><span style="color:#D73A49">-&gt;</span><span style="color:#24292E">value </span><span style="color:#D73A49">=</span><span style="color:#24292E"> expensive_result;</span></span>
<span class="line"><span style="color:#6F42C1">pthread_mutex_unlock</span><span style="color:#24292E">(</span><span style="color:#D73A49">&amp;</span><span style="color:#E36209">mutex</span><span style="color:#24292E">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D">// GOOD: compute outside lock</span></span>
<span class="line"><span style="color:#6F42C1">pthread_mutex_lock</span><span style="color:#24292E">(</span><span style="color:#D73A49">&amp;</span><span style="color:#E36209">mutex</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#24292E">data</span><span style="color:#D73A49">-&gt;</span><span style="color:#24292E">computing </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> true</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#6F42C1">pthread_mutex_unlock</span><span style="color:#24292E">(</span><span style="color:#D73A49">&amp;</span><span style="color:#E36209">mutex</span><span style="color:#24292E">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#24292E">expensive_result </span><span style="color:#D73A49">=</span><span style="color:#6F42C1"> heavy_computation</span><span style="color:#24292E">();</span><span style="color:#6A737D">  // Others can proceed</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6F42C1">pthread_mutex_lock</span><span style="color:#24292E">(</span><span style="color:#D73A49">&amp;</span><span style="color:#E36209">mutex</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#24292E">data</span><span style="color:#D73A49">-&gt;</span><span style="color:#24292E">value </span><span style="color:#D73A49">=</span><span style="color:#24292E"> expensive_result;</span></span>
<span class="line"><span style="color:#24292E">data</span><span style="color:#D73A49">-&gt;</span><span style="color:#24292E">computing </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> false</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#6F42C1">pthread_mutex_unlock</span><span style="color:#24292E">(</span><span style="color:#D73A49">&amp;</span><span style="color:#E36209">mutex</span><span style="color:#24292E">);</span></span></code></pre>
<ul>
<li>Atomic operations for hot paths: Use lock-free techniques for frequently accessed counters</li>
<li>Cache-line padding: Separate frequently modified fields to different cache lines</li>
</ul>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="c"><code><span class="line"><span style="color:#D73A49">typedef</span><span style="color:#D73A49"> struct</span><span style="color:#24292E"> {</span></span>
<span class="line"><span style="color:#D73A49">    volatile</span><span style="color:#D73A49"> int</span><span style="color:#24292E"> reader_count;</span></span>
<span class="line"><span style="color:#D73A49">    char</span><span style="color:#E36209"> pad1</span><span style="color:#24292E">[</span><span style="color:#005CC5">64</span><span style="color:#24292E">];</span><span style="color:#6A737D">  // Prevent false sharing</span></span>
<span class="line"><span style="color:#D73A49">    volatile</span><span style="color:#D73A49"> int</span><span style="color:#24292E"> writer_count;</span></span>
<span class="line"><span style="color:#D73A49">    char</span><span style="color:#E36209"> pad2</span><span style="color:#24292E">[</span><span style="color:#005CC5">64</span><span style="color:#24292E">];</span></span>
<span class="line"><span style="color:#24292E">} CounterStruct;</span></span></code></pre>
<h2 id="the-gist">The gist</h2>
<p>For the sake of being able to refer to it back without going over PostgreSQL again, below is a barebone implementation of the tricks from above.</p>
<p>Some day this might become a Ruby gem.</p>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="c"><code><span class="line"><span style="color:#6A737D">/*</span></span>
<span class="line"><span style="color:#6A737D"> * thundering_herd_demo.c</span></span>
<span class="line"><span style="color:#6A737D"> * </span></span>
<span class="line"><span style="color:#6A737D"> * Generic key/value storage with anti-thundering herd pattern</span></span>
<span class="line"><span style="color:#6A737D"> * using condition variables and shared memory.</span></span>
<span class="line"><span style="color:#6A737D"> * </span></span>
<span class="line"><span style="color:#6A737D"> * Features:</span></span>
<span class="line"><span style="color:#6A737D"> * - Key/value storage where values are msgpack-encoded bytes</span></span>
<span class="line"><span style="color:#6A737D"> * - Only one process computes expensive value per key</span></span>
<span class="line"><span style="color:#6A737D"> * - Other processes wait efficiently using condition variables</span></span>
<span class="line"><span style="color:#6A737D"> * - No PostgreSQL dependencies</span></span>
<span class="line"><span style="color:#6A737D"> */</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49">#include</span><span style="color:#032F62"> &lt;stdio.h&gt;</span></span>
<span class="line"><span style="color:#D73A49">#include</span><span style="color:#032F62"> &lt;stdlib.h&gt;</span></span>
<span class="line"><span style="color:#D73A49">#include</span><span style="color:#032F62"> &lt;stdbool.h&gt;</span></span>
<span class="line"><span style="color:#D73A49">#include</span><span style="color:#032F62"> &lt;unistd.h&gt;</span></span>
<span class="line"><span style="color:#D73A49">#include</span><span style="color:#032F62"> &lt;sys/mman.h&gt;</span></span>
<span class="line"><span style="color:#D73A49">#include</span><span style="color:#032F62"> &lt;sys/wait.h&gt;</span></span>
<span class="line"><span style="color:#D73A49">#include</span><span style="color:#032F62"> &lt;pthread.h&gt;</span></span>
<span class="line"><span style="color:#D73A49">#include</span><span style="color:#032F62"> &lt;time.h&gt;</span></span>
<span class="line"><span style="color:#D73A49">#include</span><span style="color:#032F62"> &lt;string.h&gt;</span></span>
<span class="line"><span style="color:#D73A49">#include</span><span style="color:#032F62"> &lt;stdint.h&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49">#define</span><span style="color:#6F42C1"> MAX_ENTRIES</span><span style="color:#005CC5"> 64</span></span>
<span class="line"><span style="color:#D73A49">#define</span><span style="color:#6F42C1"> MAX_KEY_SIZE</span><span style="color:#005CC5"> 48</span></span>
<span class="line"><span style="color:#D73A49">#define</span><span style="color:#6F42C1"> MAX_VALUE_SIZE</span><span style="color:#005CC5"> 1024</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49">typedef</span><span style="color:#D73A49"> struct</span><span style="color:#24292E"> {</span></span>
<span class="line"><span style="color:#D73A49">    char</span><span style="color:#E36209"> key</span><span style="color:#24292E">[MAX_KEY_SIZE];</span></span>
<span class="line"><span style="color:#D73A49">    bool</span><span style="color:#24292E"> initialized;</span></span>
<span class="line"><span style="color:#D73A49">    bool</span><span style="color:#24292E"> initializing;</span></span>
<span class="line"><span style="color:#D73A49">    pthread_mutex_t</span><span style="color:#24292E"> mutex;</span></span>
<span class="line"><span style="color:#D73A49">    pthread_cond_t</span><span style="color:#24292E"> init_cv;</span></span>
<span class="line"><span style="color:#D73A49">    time_t</span><span style="color:#24292E"> created_at;</span><span style="color:#6A737D">  // When value was computed</span></span>
<span class="line"><span style="color:#D73A49">    time_t</span><span style="color:#24292E"> ttl_seconds;</span><span style="color:#6A737D"> // TTL in seconds</span></span>
<span class="line"><span style="color:#D73A49">    size_t</span><span style="color:#24292E"> value_size;</span></span>
<span class="line"><span style="color:#D73A49">    uint8_t</span><span style="color:#E36209"> value_data</span><span style="color:#24292E">[MAX_VALUE_SIZE];</span><span style="color:#6A737D">  // msgpack-encoded bytes</span></span>
<span class="line"><span style="color:#24292E">} StorageEntry;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49">typedef</span><span style="color:#D73A49"> struct</span><span style="color:#24292E"> {</span></span>
<span class="line"><span style="color:#D73A49">    pthread_mutex_t</span><span style="color:#24292E"> global_mutex;</span></span>
<span class="line"><span style="color:#D73A49">    int</span><span style="color:#24292E"> num_entries;</span></span>
<span class="line"><span style="color:#24292E">    StorageEntry </span><span style="color:#E36209">entries</span><span style="color:#24292E">[MAX_ENTRIES];</span></span>
<span class="line"><span style="color:#24292E">} SharedStorage;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49">static</span><span style="color:#24292E"> SharedStorage </span><span style="color:#D73A49">*</span><span style="color:#24292E">storage;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D">/*</span></span>
<span class="line"><span style="color:#6A737D"> * Simple msgpack encoding for demo (just length-prefixed data)</span></span>
<span class="line"><span style="color:#6A737D"> * In real implementation, use proper msgpack library</span></span>
<span class="line"><span style="color:#6A737D"> */</span></span>
<span class="line"><span style="color:#D73A49">static</span><span style="color:#D73A49"> size_t</span><span style="color:#6F42C1"> encode_int_to_msgpack</span><span style="color:#24292E">(</span><span style="color:#D73A49">int</span><span style="color:#E36209"> value</span><span style="color:#24292E">, </span><span style="color:#D73A49">uint8_t</span><span style="color:#D73A49"> *</span><span style="color:#E36209">buffer</span><span style="color:#24292E">)</span></span>
<span class="line"><span style="color:#24292E">{</span></span>
<span class="line"><span style="color:#6A737D">    // Simple encoding: 4-byte int in network byte order</span></span>
<span class="line"><span style="color:#E36209">    buffer</span><span style="color:#24292E">[</span><span style="color:#005CC5">0</span><span style="color:#24292E">] </span><span style="color:#D73A49">=</span><span style="color:#24292E"> (value </span><span style="color:#D73A49">&gt;&gt;</span><span style="color:#005CC5"> 24</span><span style="color:#24292E">) </span><span style="color:#D73A49">&amp;</span><span style="color:#D73A49"> 0x</span><span style="color:#005CC5">FF</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#E36209">    buffer</span><span style="color:#24292E">[</span><span style="color:#005CC5">1</span><span style="color:#24292E">] </span><span style="color:#D73A49">=</span><span style="color:#24292E"> (value </span><span style="color:#D73A49">&gt;&gt;</span><span style="color:#005CC5"> 16</span><span style="color:#24292E">) </span><span style="color:#D73A49">&amp;</span><span style="color:#D73A49"> 0x</span><span style="color:#005CC5">FF</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#E36209">    buffer</span><span style="color:#24292E">[</span><span style="color:#005CC5">2</span><span style="color:#24292E">] </span><span style="color:#D73A49">=</span><span style="color:#24292E"> (value </span><span style="color:#D73A49">&gt;&gt;</span><span style="color:#005CC5"> 8</span><span style="color:#24292E">) </span><span style="color:#D73A49">&amp;</span><span style="color:#D73A49"> 0x</span><span style="color:#005CC5">FF</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#E36209">    buffer</span><span style="color:#24292E">[</span><span style="color:#005CC5">3</span><span style="color:#24292E">] </span><span style="color:#D73A49">=</span><span style="color:#24292E"> value </span><span style="color:#D73A49">&amp;</span><span style="color:#D73A49"> 0x</span><span style="color:#005CC5">FF</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#D73A49">    return</span><span style="color:#005CC5"> 4</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#24292E">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49">static</span><span style="color:#D73A49"> int</span><span style="color:#6F42C1"> decode_int_from_msgpack</span><span style="color:#24292E">(</span><span style="color:#D73A49">const</span><span style="color:#D73A49"> uint8_t</span><span style="color:#D73A49"> *</span><span style="color:#E36209">buffer</span><span style="color:#24292E">, </span><span style="color:#D73A49">size_t</span><span style="color:#E36209"> size</span><span style="color:#24292E">)</span></span>
<span class="line"><span style="color:#24292E">{</span></span>
<span class="line"><span style="color:#D73A49">    if</span><span style="color:#24292E"> (size </span><span style="color:#D73A49">&lt;</span><span style="color:#005CC5"> 4</span><span style="color:#24292E">) </span><span style="color:#D73A49">return</span><span style="color:#D73A49"> -</span><span style="color:#005CC5">1</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#D73A49">    return</span><span style="color:#24292E"> (</span><span style="color:#E36209">buffer</span><span style="color:#24292E">[</span><span style="color:#005CC5">0</span><span style="color:#24292E">] </span><span style="color:#D73A49">&lt;&lt;</span><span style="color:#005CC5"> 24</span><span style="color:#24292E">) </span><span style="color:#D73A49">|</span><span style="color:#24292E"> (</span><span style="color:#E36209">buffer</span><span style="color:#24292E">[</span><span style="color:#005CC5">1</span><span style="color:#24292E">] </span><span style="color:#D73A49">&lt;&lt;</span><span style="color:#005CC5"> 16</span><span style="color:#24292E">) </span><span style="color:#D73A49">|</span><span style="color:#24292E"> (</span><span style="color:#E36209">buffer</span><span style="color:#24292E">[</span><span style="color:#005CC5">2</span><span style="color:#24292E">] </span><span style="color:#D73A49">&lt;&lt;</span><span style="color:#005CC5"> 8</span><span style="color:#24292E">) </span><span style="color:#D73A49">|</span><span style="color:#E36209"> buffer</span><span style="color:#24292E">[</span><span style="color:#005CC5">3</span><span style="color:#24292E">];</span></span>
<span class="line"><span style="color:#24292E">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D">/*</span></span>
<span class="line"><span style="color:#6A737D"> * Check if entry is expired based on TTL</span></span>
<span class="line"><span style="color:#6A737D"> */</span></span>
<span class="line"><span style="color:#D73A49">static</span><span style="color:#D73A49"> bool</span><span style="color:#6F42C1"> is_entry_expired</span><span style="color:#24292E">(StorageEntry </span><span style="color:#D73A49">*</span><span style="color:#E36209">entry</span><span style="color:#24292E">)</span></span>
<span class="line"><span style="color:#24292E">{</span></span>
<span class="line"><span style="color:#D73A49">    if</span><span style="color:#24292E"> (</span><span style="color:#D73A49">!</span><span style="color:#24292E">entry-&gt;initialized) </span><span style="color:#D73A49">return</span><span style="color:#005CC5"> false</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#24292E">    </span></span>
<span class="line"><span style="color:#D73A49">    time_t</span><span style="color:#24292E"> now </span><span style="color:#D73A49">=</span><span style="color:#6F42C1"> time</span><span style="color:#24292E">(</span><span style="color:#005CC5">NULL</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#D73A49">    return</span><span style="color:#24292E"> (now </span><span style="color:#D73A49">-</span><span style="color:#24292E"> entry-&gt;created_at) </span><span style="color:#D73A49">&gt;</span><span style="color:#24292E"> entry-&gt;ttl_seconds;</span></span>
<span class="line"><span style="color:#24292E">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D">/*</span></span>
<span class="line"><span style="color:#6A737D"> * Find or create entry for given key</span></span>
<span class="line"><span style="color:#6A737D"> */</span></span>
<span class="line"><span style="color:#D73A49">static</span><span style="color:#24292E"> StorageEntry</span><span style="color:#D73A49">*</span><span style="color:#6F42C1"> find_or_create_entry</span><span style="color:#24292E">(</span><span style="color:#D73A49">const</span><span style="color:#D73A49"> char</span><span style="color:#D73A49"> *</span><span style="color:#E36209">key</span><span style="color:#24292E">)</span></span>
<span class="line"><span style="color:#24292E">{</span></span>
<span class="line"><span style="color:#6F42C1">    pthread_mutex_lock</span><span style="color:#24292E">(</span><span style="color:#D73A49">&amp;</span><span style="color:#24292E">storage-&gt;global_mutex);</span></span>
<span class="line"><span style="color:#24292E">    </span></span>
<span class="line"><span style="color:#6A737D">    // Look for existing entry</span></span>
<span class="line"><span style="color:#D73A49">    for</span><span style="color:#24292E"> (</span><span style="color:#D73A49">int</span><span style="color:#24292E"> i </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> 0</span><span style="color:#24292E">; i </span><span style="color:#D73A49">&lt;</span><span style="color:#24292E"> storage-&gt;num_entries; i</span><span style="color:#D73A49">++</span><span style="color:#24292E">) {</span></span>
<span class="line"><span style="color:#D73A49">        if</span><span style="color:#24292E"> (</span><span style="color:#6F42C1">strcmp</span><span style="color:#24292E">(storage-&gt;entries[i].key, key) </span><span style="color:#D73A49">==</span><span style="color:#005CC5"> 0</span><span style="color:#24292E">) {</span></span>
<span class="line"><span style="color:#6F42C1">            pthread_mutex_unlock</span><span style="color:#24292E">(</span><span style="color:#D73A49">&amp;</span><span style="color:#24292E">storage-&gt;global_mutex);</span></span>
<span class="line"><span style="color:#D73A49">            return</span><span style="color:#D73A49"> &amp;</span><span style="color:#24292E">storage-&gt;entries[i];</span></span>
<span class="line"><span style="color:#24292E">        }</span></span>
<span class="line"><span style="color:#24292E">    }</span></span>
<span class="line"><span style="color:#24292E">    </span></span>
<span class="line"><span style="color:#6A737D">    // Create new entry if space available</span></span>
<span class="line"><span style="color:#D73A49">    if</span><span style="color:#24292E"> (storage-&gt;num_entries </span><span style="color:#D73A49">&lt;</span><span style="color:#24292E"> MAX_ENTRIES) {</span></span>
<span class="line"><span style="color:#24292E">        StorageEntry </span><span style="color:#D73A49">*</span><span style="color:#24292E">entry </span><span style="color:#D73A49">=</span><span style="color:#D73A49"> &amp;</span><span style="color:#24292E">storage-&gt;entries[storage-&gt;num_entries];</span></span>
<span class="line"><span style="color:#6F42C1">        strncpy</span><span style="color:#24292E">(entry-&gt;key, key, MAX_KEY_SIZE </span><span style="color:#D73A49">-</span><span style="color:#005CC5"> 1</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#24292E">        entry-&gt;key[MAX_KEY_SIZE </span><span style="color:#D73A49">-</span><span style="color:#005CC5"> 1</span><span style="color:#24292E">] </span><span style="color:#D73A49">=</span><span style="color:#032F62"> &#39;</span><span style="color:#005CC5">\0</span><span style="color:#032F62">&#39;</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#24292E">        entry-&gt;initialized </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> false</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#24292E">        entry-&gt;initializing </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> false</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#24292E">        entry-&gt;created_at </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> 0</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#24292E">        entry-&gt;ttl_seconds </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> 3</span><span style="color:#24292E">;</span><span style="color:#6A737D">  // Default 3 second TTL</span></span>
<span class="line"><span style="color:#24292E">        entry-&gt;value_size </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> 0</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#24292E">        </span></span>
<span class="line"><span style="color:#6A737D">        // Initialize per-entry synchronization</span></span>
<span class="line"><span style="color:#D73A49">        pthread_mutexattr_t</span><span style="color:#24292E"> mutex_attr;</span></span>
<span class="line"><span style="color:#6F42C1">        pthread_mutexattr_init</span><span style="color:#24292E">(</span><span style="color:#D73A49">&amp;</span><span style="color:#24292E">mutex_attr);</span></span>
<span class="line"><span style="color:#6F42C1">        pthread_mutexattr_setpshared</span><span style="color:#24292E">(</span><span style="color:#D73A49">&amp;</span><span style="color:#24292E">mutex_attr, PTHREAD_PROCESS_SHARED);</span></span>
<span class="line"><span style="color:#6F42C1">        pthread_mutex_init</span><span style="color:#24292E">(</span><span style="color:#D73A49">&amp;</span><span style="color:#24292E">entry-&gt;mutex, </span><span style="color:#D73A49">&amp;</span><span style="color:#24292E">mutex_attr);</span></span>
<span class="line"><span style="color:#24292E">        </span></span>
<span class="line"><span style="color:#D73A49">        pthread_condattr_t</span><span style="color:#24292E"> cond_attr;</span></span>
<span class="line"><span style="color:#6F42C1">        pthread_condattr_init</span><span style="color:#24292E">(</span><span style="color:#D73A49">&amp;</span><span style="color:#24292E">cond_attr);</span></span>
<span class="line"><span style="color:#6F42C1">        pthread_condattr_setpshared</span><span style="color:#24292E">(</span><span style="color:#D73A49">&amp;</span><span style="color:#24292E">cond_attr, PTHREAD_PROCESS_SHARED);</span></span>
<span class="line"><span style="color:#6F42C1">        pthread_cond_init</span><span style="color:#24292E">(</span><span style="color:#D73A49">&amp;</span><span style="color:#24292E">entry-&gt;init_cv, </span><span style="color:#D73A49">&amp;</span><span style="color:#24292E">cond_attr);</span></span>
<span class="line"><span style="color:#24292E">        </span></span>
<span class="line"><span style="color:#24292E">        storage-&gt;num_entries</span><span style="color:#D73A49">++</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#6F42C1">        pthread_mutex_unlock</span><span style="color:#24292E">(</span><span style="color:#D73A49">&amp;</span><span style="color:#24292E">storage-&gt;global_mutex);</span></span>
<span class="line"><span style="color:#D73A49">        return</span><span style="color:#24292E"> entry;</span></span>
<span class="line"><span style="color:#24292E">    }</span></span>
<span class="line"><span style="color:#24292E">    </span></span>
<span class="line"><span style="color:#6F42C1">    pthread_mutex_unlock</span><span style="color:#24292E">(</span><span style="color:#D73A49">&amp;</span><span style="color:#24292E">storage-&gt;global_mutex);</span></span>
<span class="line"><span style="color:#D73A49">    return</span><span style="color:#005CC5"> NULL</span><span style="color:#24292E">;</span><span style="color:#6A737D">  // No space</span></span>
<span class="line"><span style="color:#24292E">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D">/*</span></span>
<span class="line"><span style="color:#6A737D"> * Simulate expensive calculation for a given key</span></span>
<span class="line"><span style="color:#6A737D"> */</span></span>
<span class="line"><span style="color:#D73A49">static</span><span style="color:#D73A49"> int</span><span style="color:#6F42C1"> calculate_expensive_value</span><span style="color:#24292E">(</span><span style="color:#D73A49">const</span><span style="color:#D73A49"> char</span><span style="color:#D73A49"> *</span><span style="color:#E36209">key</span><span style="color:#24292E">, </span><span style="color:#D73A49">int</span><span style="color:#E36209"> worker_id</span><span style="color:#24292E">)</span></span>
<span class="line"><span style="color:#24292E">{</span></span>
<span class="line"><span style="color:#6F42C1">    printf</span><span style="color:#24292E">(</span><span style="color:#032F62">&#34;Worker </span><span style="color:#005CC5">%d</span><span style="color:#032F62">: Computing expensive value for key &#39;</span><span style="color:#005CC5">%s</span><span style="color:#032F62">&#39;...</span><span style="color:#005CC5">\n</span><span style="color:#032F62">&#34;</span><span style="color:#24292E">, worker_id, key);</span></span>
<span class="line"><span style="color:#6F42C1">    sleep</span><span style="color:#24292E">(</span><span style="color:#005CC5">1</span><span style="color:#24292E">);</span><span style="color:#6A737D">  // Simulate expensive work</span></span>
<span class="line"><span style="color:#24292E">    </span></span>
<span class="line"><span style="color:#6A737D">    // Generate &#34;expensive&#34; result based on key hash</span></span>
<span class="line"><span style="color:#D73A49">    int</span><span style="color:#24292E"> hash </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> 0</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#D73A49">    for</span><span style="color:#24292E"> (</span><span style="color:#D73A49">const</span><span style="color:#D73A49"> char</span><span style="color:#D73A49"> *</span><span style="color:#24292E">p </span><span style="color:#D73A49">=</span><span style="color:#24292E"> key; </span><span style="color:#D73A49">*</span><span style="color:#24292E">p; p</span><span style="color:#D73A49">++</span><span style="color:#24292E">) {</span></span>
<span class="line"><span style="color:#24292E">        hash </span><span style="color:#D73A49">=</span><span style="color:#24292E"> hash </span><span style="color:#D73A49">*</span><span style="color:#005CC5"> 31</span><span style="color:#D73A49"> +</span><span style="color:#D73A49"> *</span><span style="color:#24292E">p;</span></span>
<span class="line"><span style="color:#24292E">    }</span></span>
<span class="line"><span style="color:#D73A49">    int</span><span style="color:#24292E"> result </span><span style="color:#D73A49">=</span><span style="color:#6F42C1"> abs</span><span style="color:#24292E">(hash) </span><span style="color:#D73A49">%</span><span style="color:#005CC5"> 1000</span><span style="color:#D73A49"> +</span><span style="color:#24292E"> worker_id;</span></span>
<span class="line"><span style="color:#24292E">    </span></span>
<span class="line"><span style="color:#6F42C1">    printf</span><span style="color:#24292E">(</span><span style="color:#032F62">&#34;Worker </span><span style="color:#005CC5">%d</span><span style="color:#032F62">: Finished computing for key &#39;</span><span style="color:#005CC5">%s</span><span style="color:#032F62">&#39;, result = </span><span style="color:#005CC5">%d\n</span><span style="color:#032F62">&#34;</span><span style="color:#24292E">, </span></span>
<span class="line"><span style="color:#24292E">           worker_id, key, result);</span></span>
<span class="line"><span style="color:#D73A49">    return</span><span style="color:#24292E"> result;</span></span>
<span class="line"><span style="color:#24292E">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D">/*</span></span>
<span class="line"><span style="color:#6A737D"> * Get value for key, computing it if necessary (anti-thundering herd)</span></span>
<span class="line"><span style="color:#6A737D"> * Supports TTL - expired entries are treated as cache misses</span></span>
<span class="line"><span style="color:#6A737D"> */</span></span>
<span class="line"><span style="color:#D73A49">static</span><span style="color:#D73A49"> int</span><span style="color:#6F42C1"> get_value</span><span style="color:#24292E">(</span><span style="color:#D73A49">const</span><span style="color:#D73A49"> char</span><span style="color:#D73A49"> *</span><span style="color:#E36209">key</span><span style="color:#24292E">, </span><span style="color:#D73A49">int</span><span style="color:#E36209"> worker_id</span><span style="color:#24292E">)</span></span>
<span class="line"><span style="color:#24292E">{</span></span>
<span class="line"><span style="color:#24292E">    StorageEntry </span><span style="color:#D73A49">*</span><span style="color:#24292E">entry </span><span style="color:#D73A49">=</span><span style="color:#6F42C1"> find_or_create_entry</span><span style="color:#24292E">(key);</span></span>
<span class="line"><span style="color:#D73A49">    if</span><span style="color:#24292E"> (</span><span style="color:#D73A49">!</span><span style="color:#24292E">entry) {</span></span>
<span class="line"><span style="color:#6F42C1">        printf</span><span style="color:#24292E">(</span><span style="color:#032F62">&#34;Worker </span><span style="color:#005CC5">%d</span><span style="color:#032F62">: No space for key &#39;</span><span style="color:#005CC5">%s</span><span style="color:#032F62">&#39;</span><span style="color:#005CC5">\n</span><span style="color:#032F62">&#34;</span><span style="color:#24292E">, worker_id, key);</span></span>
<span class="line"><span style="color:#D73A49">        return</span><span style="color:#D73A49"> -</span><span style="color:#005CC5">1</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#24292E">    }</span></span>
<span class="line"><span style="color:#24292E">    </span></span>
<span class="line"><span style="color:#6F42C1">    pthread_mutex_lock</span><span style="color:#24292E">(</span><span style="color:#D73A49">&amp;</span><span style="color:#24292E">entry-&gt;mutex);</span></span>
<span class="line"><span style="color:#24292E">    </span></span>
<span class="line"><span style="color:#D73A49">    if</span><span style="color:#24292E"> (entry-&gt;initialized </span><span style="color:#D73A49">&amp;&amp;</span><span style="color:#D73A49"> !</span><span style="color:#6F42C1">is_entry_expired</span><span style="color:#24292E">(entry)) {</span></span>
<span class="line"><span style="color:#6A737D">        // Value already computed and still fresh</span></span>
<span class="line"><span style="color:#D73A49">        int</span><span style="color:#24292E"> result </span><span style="color:#D73A49">=</span><span style="color:#6F42C1"> decode_int_from_msgpack</span><span style="color:#24292E">(entry-&gt;value_data, entry-&gt;value_size);</span></span>
<span class="line"><span style="color:#D73A49">        time_t</span><span style="color:#24292E"> age </span><span style="color:#D73A49">=</span><span style="color:#6F42C1"> time</span><span style="color:#24292E">(</span><span style="color:#005CC5">NULL</span><span style="color:#24292E">) </span><span style="color:#D73A49">-</span><span style="color:#24292E"> entry-&gt;created_at;</span></span>
<span class="line"><span style="color:#6F42C1">        pthread_mutex_unlock</span><span style="color:#24292E">(</span><span style="color:#D73A49">&amp;</span><span style="color:#24292E">entry-&gt;mutex);</span></span>
<span class="line"><span style="color:#6F42C1">        printf</span><span style="color:#24292E">(</span><span style="color:#032F62">&#34;Worker </span><span style="color:#005CC5">%d</span><span style="color:#032F62">: Key &#39;</span><span style="color:#005CC5">%s</span><span style="color:#032F62">&#39; cache hit, result = </span><span style="color:#005CC5">%d</span><span style="color:#032F62"> (age: </span><span style="color:#005CC5">%ld</span><span style="color:#032F62">s)</span><span style="color:#005CC5">\n</span><span style="color:#032F62">&#34;</span><span style="color:#24292E">, </span></span>
<span class="line"><span style="color:#24292E">               worker_id, key, result, age);</span></span>
<span class="line"><span style="color:#D73A49">        return</span><span style="color:#24292E"> result;</span></span>
<span class="line"><span style="color:#24292E">    }</span></span>
<span class="line"><span style="color:#24292E">    </span></span>
<span class="line"><span style="color:#D73A49">    if</span><span style="color:#24292E"> (entry-&gt;initialized </span><span style="color:#D73A49">&amp;&amp;</span><span style="color:#6F42C1"> is_entry_expired</span><span style="color:#24292E">(entry)) {</span></span>
<span class="line"><span style="color:#6A737D">        // Value expired - treat as cache miss</span></span>
<span class="line"><span style="color:#6F42C1">        printf</span><span style="color:#24292E">(</span><span style="color:#032F62">&#34;Worker </span><span style="color:#005CC5">%d</span><span style="color:#032F62">: Key &#39;</span><span style="color:#005CC5">%s</span><span style="color:#032F62">&#39; expired (age: </span><span style="color:#005CC5">%ld</span><span style="color:#032F62">s &gt; </span><span style="color:#005CC5">%ld</span><span style="color:#032F62">s TTL), will recompute</span><span style="color:#005CC5">\n</span><span style="color:#032F62">&#34;</span><span style="color:#24292E">, </span></span>
<span class="line"><span style="color:#24292E">               worker_id, key, </span><span style="color:#6F42C1">time</span><span style="color:#24292E">(</span><span style="color:#005CC5">NULL</span><span style="color:#24292E">) </span><span style="color:#D73A49">-</span><span style="color:#24292E"> entry-&gt;created_at, entry-&gt;ttl_seconds);</span></span>
<span class="line"><span style="color:#24292E">        entry-&gt;initialized </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> false</span><span style="color:#24292E">;</span><span style="color:#6A737D">  // Mark as needing recomputation</span></span>
<span class="line"><span style="color:#24292E">    }</span></span>
<span class="line"><span style="color:#24292E">    </span></span>
<span class="line"><span style="color:#D73A49">    if</span><span style="color:#24292E"> (entry-&gt;initializing) {</span></span>
<span class="line"><span style="color:#6A737D">        // Someone else is computing - wait</span></span>
<span class="line"><span style="color:#6F42C1">        printf</span><span style="color:#24292E">(</span><span style="color:#032F62">&#34;Worker </span><span style="color:#005CC5">%d</span><span style="color:#032F62">: Key &#39;</span><span style="color:#005CC5">%s</span><span style="color:#032F62">&#39; being computed by another worker, waiting...</span><span style="color:#005CC5">\n</span><span style="color:#032F62">&#34;</span><span style="color:#24292E">, </span></span>
<span class="line"><span style="color:#24292E">               worker_id, key);</span></span>
<span class="line"><span style="color:#24292E">        </span></span>
<span class="line"><span style="color:#D73A49">        while</span><span style="color:#24292E"> (</span><span style="color:#D73A49">!</span><span style="color:#24292E">entry-&gt;initialized) {</span></span>
<span class="line"><span style="color:#6F42C1">            pthread_cond_wait</span><span style="color:#24292E">(</span><span style="color:#D73A49">&amp;</span><span style="color:#24292E">entry-&gt;init_cv, </span><span style="color:#D73A49">&amp;</span><span style="color:#24292E">entry-&gt;mutex);</span></span>
<span class="line"><span style="color:#24292E">        }</span></span>
<span class="line"><span style="color:#24292E">        </span></span>
<span class="line"><span style="color:#D73A49">        int</span><span style="color:#24292E"> result </span><span style="color:#D73A49">=</span><span style="color:#6F42C1"> decode_int_from_msgpack</span><span style="color:#24292E">(entry-&gt;value_data, entry-&gt;value_size);</span></span>
<span class="line"><span style="color:#6F42C1">        pthread_mutex_unlock</span><span style="color:#24292E">(</span><span style="color:#D73A49">&amp;</span><span style="color:#24292E">entry-&gt;mutex);</span></span>
<span class="line"><span style="color:#6F42C1">        printf</span><span style="color:#24292E">(</span><span style="color:#032F62">&#34;Worker </span><span style="color:#005CC5">%d</span><span style="color:#032F62">: Key &#39;</span><span style="color:#005CC5">%s</span><span style="color:#032F62">&#39; computation complete, result = </span><span style="color:#005CC5">%d\n</span><span style="color:#032F62">&#34;</span><span style="color:#24292E">, </span></span>
<span class="line"><span style="color:#24292E">               worker_id, key, result);</span></span>
<span class="line"><span style="color:#D73A49">        return</span><span style="color:#24292E"> result;</span></span>
<span class="line"><span style="color:#24292E">    }</span></span>
<span class="line"><span style="color:#24292E">    </span></span>
<span class="line"><span style="color:#6A737D">    // We won the race - compute the value</span></span>
<span class="line"><span style="color:#6F42C1">    printf</span><span style="color:#24292E">(</span><span style="color:#032F62">&#34;Worker </span><span style="color:#005CC5">%d</span><span style="color:#032F62">: Won race for key &#39;</span><span style="color:#005CC5">%s</span><span style="color:#032F62">&#39;, will compute</span><span style="color:#005CC5">\n</span><span style="color:#032F62">&#34;</span><span style="color:#24292E">, worker_id, key);</span></span>
<span class="line"><span style="color:#24292E">    entry-&gt;initializing </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> true</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#6F42C1">    pthread_mutex_unlock</span><span style="color:#24292E">(</span><span style="color:#D73A49">&amp;</span><span style="color:#24292E">entry-&gt;mutex);</span></span>
<span class="line"><span style="color:#24292E">    </span></span>
<span class="line"><span style="color:#6A737D">    // Do expensive computation outside the lock</span></span>
<span class="line"><span style="color:#D73A49">    int</span><span style="color:#24292E"> computed_value </span><span style="color:#D73A49">=</span><span style="color:#6F42C1"> calculate_expensive_value</span><span style="color:#24292E">(key, worker_id);</span></span>
<span class="line"><span style="color:#24292E">    </span></span>
<span class="line"><span style="color:#6A737D">    // Encode result as msgpack and store</span></span>
<span class="line"><span style="color:#D73A49">    uint8_t</span><span style="color:#E36209"> encoded_data</span><span style="color:#24292E">[MAX_VALUE_SIZE];</span></span>
<span class="line"><span style="color:#D73A49">    size_t</span><span style="color:#24292E"> encoded_size </span><span style="color:#D73A49">=</span><span style="color:#6F42C1"> encode_int_to_msgpack</span><span style="color:#24292E">(computed_value, encoded_data);</span></span>
<span class="line"><span style="color:#24292E">    </span></span>
<span class="line"><span style="color:#6F42C1">    pthread_mutex_lock</span><span style="color:#24292E">(</span><span style="color:#D73A49">&amp;</span><span style="color:#24292E">entry-&gt;mutex);</span></span>
<span class="line"><span style="color:#6F42C1">    memcpy</span><span style="color:#24292E">(entry-&gt;value_data, encoded_data, encoded_size);</span></span>
<span class="line"><span style="color:#24292E">    entry-&gt;value_size </span><span style="color:#D73A49">=</span><span style="color:#24292E"> encoded_size;</span></span>
<span class="line"><span style="color:#24292E">    entry-&gt;created_at </span><span style="color:#D73A49">=</span><span style="color:#6F42C1"> time</span><span style="color:#24292E">(</span><span style="color:#005CC5">NULL</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#24292E">    entry-&gt;initialized </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> true</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#6F42C1">    pthread_mutex_unlock</span><span style="color:#24292E">(</span><span style="color:#D73A49">&amp;</span><span style="color:#24292E">entry-&gt;mutex);</span></span>
<span class="line"><span style="color:#24292E">    </span></span>
<span class="line"><span style="color:#6A737D">    // Wake up all waiting processes</span></span>
<span class="line"><span style="color:#6F42C1">    pthread_cond_broadcast</span><span style="color:#24292E">(</span><span style="color:#D73A49">&amp;</span><span style="color:#24292E">entry-&gt;init_cv);</span></span>
<span class="line"><span style="color:#6F42C1">    printf</span><span style="color:#24292E">(</span><span style="color:#032F62">&#34;Worker </span><span style="color:#005CC5">%d</span><span style="color:#032F62">: Stored result for key &#39;</span><span style="color:#005CC5">%s</span><span style="color:#032F62">&#39;, notified waiters</span><span style="color:#005CC5">\n</span><span style="color:#032F62">&#34;</span><span style="color:#24292E">, </span></span>
<span class="line"><span style="color:#24292E">           worker_id, key);</span></span>
<span class="line"><span style="color:#24292E">    </span></span>
<span class="line"><span style="color:#D73A49">    return</span><span style="color:#24292E"> computed_value;</span></span>
<span class="line"><span style="color:#24292E">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D">/*</span></span>
<span class="line"><span style="color:#6A737D"> * Worker process that requests multiple keys</span></span>
<span class="line"><span style="color:#6A737D"> */</span></span>
<span class="line"><span style="color:#D73A49">static</span><span style="color:#D73A49"> void</span><span style="color:#6F42C1"> worker_process</span><span style="color:#24292E">(</span><span style="color:#D73A49">int</span><span style="color:#E36209"> worker_id</span><span style="color:#24292E">)</span></span>
<span class="line"><span style="color:#24292E">{</span></span>
<span class="line"><span style="color:#6F42C1">    printf</span><span style="color:#24292E">(</span><span style="color:#032F62">&#34;Worker </span><span style="color:#005CC5">%d</span><span style="color:#032F62">: Started (PID </span><span style="color:#005CC5">%d</span><span style="color:#032F62">)</span><span style="color:#005CC5">\n</span><span style="color:#032F62">&#34;</span><span style="color:#24292E">, worker_id, </span><span style="color:#6F42C1">getpid</span><span style="color:#24292E">());</span></span>
<span class="line"><span style="color:#24292E">    </span></span>
<span class="line"><span style="color:#6A737D">    // Each worker requests different combinations of keys</span></span>
<span class="line"><span style="color:#D73A49">    const</span><span style="color:#D73A49"> char</span><span style="color:#D73A49"> *</span><span style="color:#24292E">keys</span><span style="color:#D73A49">[]</span><span style="color:#D73A49"> =</span><span style="color:#24292E"> {</span><span style="color:#032F62">&#34;config&#34;</span><span style="color:#24292E">, </span><span style="color:#032F62">&#34;stats&#34;</span><span style="color:#24292E">, </span><span style="color:#032F62">&#34;cache&#34;</span><span style="color:#24292E">, </span><span style="color:#032F62">&#34;metadata&#34;</span><span style="color:#24292E">};</span></span>
<span class="line"><span style="color:#D73A49">    int</span><span style="color:#24292E"> num_keys </span><span style="color:#D73A49">=</span><span style="color:#D73A49"> sizeof</span><span style="color:#24292E">(keys) </span><span style="color:#D73A49">/</span><span style="color:#D73A49"> sizeof</span><span style="color:#24292E">(</span><span style="color:#E36209">keys</span><span style="color:#24292E">[</span><span style="color:#005CC5">0</span><span style="color:#24292E">]);</span></span>
<span class="line"><span style="color:#24292E">    </span></span>
<span class="line"><span style="color:#6A737D">    // Add random delay to increase race conditions</span></span>
<span class="line"><span style="color:#6F42C1">    usleep</span><span style="color:#24292E">((</span><span style="color:#6F42C1">rand</span><span style="color:#24292E">() </span><span style="color:#D73A49">%</span><span style="color:#005CC5"> 200</span><span style="color:#24292E">) </span><span style="color:#D73A49">*</span><span style="color:#005CC5"> 1000</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#24292E">    </span></span>
<span class="line"><span style="color:#6A737D">    // Request 2-3 keys per worker</span></span>
<span class="line"><span style="color:#D73A49">    int</span><span style="color:#24292E"> start_key </span><span style="color:#D73A49">=</span><span style="color:#24292E"> worker_id </span><span style="color:#D73A49">%</span><span style="color:#24292E"> num_keys;</span></span>
<span class="line"><span style="color:#D73A49">    int</span><span style="color:#24292E"> num_requests </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> 2</span><span style="color:#D73A49"> +</span><span style="color:#24292E"> (worker_id </span><span style="color:#D73A49">%</span><span style="color:#005CC5"> 2</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#24292E">    </span></span>
<span class="line"><span style="color:#D73A49">    for</span><span style="color:#24292E"> (</span><span style="color:#D73A49">int</span><span style="color:#24292E"> i </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> 0</span><span style="color:#24292E">; i </span><span style="color:#D73A49">&lt;</span><span style="color:#24292E"> num_requests; i</span><span style="color:#D73A49">++</span><span style="color:#24292E">) {</span></span>
<span class="line"><span style="color:#D73A49">        const</span><span style="color:#D73A49"> char</span><span style="color:#D73A49"> *</span><span style="color:#24292E">key </span><span style="color:#D73A49">=</span><span style="color:#E36209"> keys</span><span style="color:#24292E">[(start_key </span><span style="color:#D73A49">+</span><span style="color:#24292E"> i) </span><span style="color:#D73A49">%</span><span style="color:#24292E"> num_keys];</span></span>
<span class="line"><span style="color:#D73A49">        int</span><span style="color:#24292E"> result </span><span style="color:#D73A49">=</span><span style="color:#6F42C1"> get_value</span><span style="color:#24292E">(key, worker_id);</span></span>
<span class="line"><span style="color:#6F42C1">        printf</span><span style="color:#24292E">(</span><span style="color:#032F62">&#34;Worker </span><span style="color:#005CC5">%d</span><span style="color:#032F62">: Got result </span><span style="color:#005CC5">%d</span><span style="color:#032F62"> for key &#39;</span><span style="color:#005CC5">%s</span><span style="color:#032F62">&#39;</span><span style="color:#005CC5">\n</span><span style="color:#032F62">&#34;</span><span style="color:#24292E">, worker_id, result, key);</span></span>
<span class="line"><span style="color:#24292E">        </span></span>
<span class="line"><span style="color:#6A737D">        // Small delay between requests</span></span>
<span class="line"><span style="color:#6F42C1">        usleep</span><span style="color:#24292E">(</span><span style="color:#005CC5">100000</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#24292E">    }</span></span>
<span class="line"><span style="color:#24292E">    </span></span>
<span class="line"><span style="color:#6A737D">    // Wait a bit, then re-request same keys to test TTL</span></span>
<span class="line"><span style="color:#6F42C1">    printf</span><span style="color:#24292E">(</span><span style="color:#032F62">&#34;Worker </span><span style="color:#005CC5">%d</span><span style="color:#032F62">: Waiting 4 seconds to test TTL expiration...</span><span style="color:#005CC5">\n</span><span style="color:#032F62">&#34;</span><span style="color:#24292E">, worker_id);</span></span>
<span class="line"><span style="color:#6F42C1">    sleep</span><span style="color:#24292E">(</span><span style="color:#005CC5">4</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#24292E">    </span></span>
<span class="line"><span style="color:#6F42C1">    printf</span><span style="color:#24292E">(</span><span style="color:#032F62">&#34;Worker </span><span style="color:#005CC5">%d</span><span style="color:#032F62">: Re-requesting keys after TTL expiration...</span><span style="color:#005CC5">\n</span><span style="color:#032F62">&#34;</span><span style="color:#24292E">, worker_id);</span></span>
<span class="line"><span style="color:#D73A49">    for</span><span style="color:#24292E"> (</span><span style="color:#D73A49">int</span><span style="color:#24292E"> i </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> 0</span><span style="color:#24292E">; i </span><span style="color:#D73A49">&lt;</span><span style="color:#24292E"> num_requests; i</span><span style="color:#D73A49">++</span><span style="color:#24292E">) {</span></span>
<span class="line"><span style="color:#D73A49">        const</span><span style="color:#D73A49"> char</span><span style="color:#D73A49"> *</span><span style="color:#24292E">key </span><span style="color:#D73A49">=</span><span style="color:#E36209"> keys</span><span style="color:#24292E">[(start_key </span><span style="color:#D73A49">+</span><span style="color:#24292E"> i) </span><span style="color:#D73A49">%</span><span style="color:#24292E"> num_keys];</span></span>
<span class="line"><span style="color:#D73A49">        int</span><span style="color:#24292E"> result </span><span style="color:#D73A49">=</span><span style="color:#6F42C1"> get_value</span><span style="color:#24292E">(key, worker_id);</span></span>
<span class="line"><span style="color:#6F42C1">        printf</span><span style="color:#24292E">(</span><span style="color:#032F62">&#34;Worker </span><span style="color:#005CC5">%d</span><span style="color:#032F62">: Re-got result </span><span style="color:#005CC5">%d</span><span style="color:#032F62"> for key &#39;</span><span style="color:#005CC5">%s</span><span style="color:#032F62">&#39;</span><span style="color:#005CC5">\n</span><span style="color:#032F62">&#34;</span><span style="color:#24292E">, worker_id, result, key);</span></span>
<span class="line"><span style="color:#24292E">    }</span></span>
<span class="line"><span style="color:#24292E">    </span></span>
<span class="line"><span style="color:#6F42C1">    printf</span><span style="color:#24292E">(</span><span style="color:#032F62">&#34;Worker </span><span style="color:#005CC5">%d</span><span style="color:#032F62">: Completed all requests</span><span style="color:#005CC5">\n</span><span style="color:#032F62">&#34;</span><span style="color:#24292E">, worker_id);</span></span>
<span class="line"><span style="color:#24292E">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D">/*</span></span>
<span class="line"><span style="color:#6A737D"> * Print final storage state</span></span>
<span class="line"><span style="color:#6A737D"> */</span></span>
<span class="line"><span style="color:#D73A49">static</span><span style="color:#D73A49"> void</span><span style="color:#6F42C1"> print_storage_state</span><span style="color:#24292E">(</span><span style="color:#D73A49">void</span><span style="color:#24292E">)</span></span>
<span class="line"><span style="color:#24292E">{</span></span>
<span class="line"><span style="color:#6F42C1">    printf</span><span style="color:#24292E">(</span><span style="color:#032F62">&#34;</span><span style="color:#005CC5">\n</span><span style="color:#032F62">=== Final Storage State ===</span><span style="color:#005CC5">\n</span><span style="color:#032F62">&#34;</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#D73A49">    time_t</span><span style="color:#24292E"> now </span><span style="color:#D73A49">=</span><span style="color:#6F42C1"> time</span><span style="color:#24292E">(</span><span style="color:#005CC5">NULL</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#D73A49">    for</span><span style="color:#24292E"> (</span><span style="color:#D73A49">int</span><span style="color:#24292E"> i </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> 0</span><span style="color:#24292E">; i </span><span style="color:#D73A49">&lt;</span><span style="color:#24292E"> storage-&gt;num_entries; i</span><span style="color:#D73A49">++</span><span style="color:#24292E">) {</span></span>
<span class="line"><span style="color:#24292E">        StorageEntry </span><span style="color:#D73A49">*</span><span style="color:#24292E">entry </span><span style="color:#D73A49">=</span><span style="color:#D73A49"> &amp;</span><span style="color:#24292E">storage-&gt;entries[i];</span></span>
<span class="line"><span style="color:#D73A49">        if</span><span style="color:#24292E"> (entry-&gt;initialized) {</span></span>
<span class="line"><span style="color:#D73A49">            int</span><span style="color:#24292E"> value </span><span style="color:#D73A49">=</span><span style="color:#6F42C1"> decode_int_from_msgpack</span><span style="color:#24292E">(entry-&gt;value_data, entry-&gt;value_size);</span></span>
<span class="line"><span style="color:#D73A49">            time_t</span><span style="color:#24292E"> age </span><span style="color:#D73A49">=</span><span style="color:#24292E"> now </span><span style="color:#D73A49">-</span><span style="color:#24292E"> entry-&gt;created_at;</span></span>
<span class="line"><span style="color:#D73A49">            bool</span><span style="color:#24292E"> expired </span><span style="color:#D73A49">=</span><span style="color:#6F42C1"> is_entry_expired</span><span style="color:#24292E">(entry);</span></span>
<span class="line"><span style="color:#6F42C1">            printf</span><span style="color:#24292E">(</span><span style="color:#032F62">&#34;Key: &#39;</span><span style="color:#005CC5">%s</span><span style="color:#032F62">&#39; -&gt; Value: </span><span style="color:#005CC5">%d</span><span style="color:#032F62"> (age: </span><span style="color:#005CC5">%ld</span><span style="color:#032F62">s, TTL: </span><span style="color:#005CC5">%ld</span><span style="color:#032F62">s) </span><span style="color:#005CC5">%s\n</span><span style="color:#032F62">&#34;</span><span style="color:#24292E">, </span></span>
<span class="line"><span style="color:#24292E">                   entry-&gt;key, value, age, entry-&gt;ttl_seconds, </span></span>
<span class="line"><span style="color:#24292E">                   expired </span><span style="color:#D73A49">?</span><span style="color:#032F62"> &#34;[EXPIRED]&#34;</span><span style="color:#D73A49"> :</span><span style="color:#032F62"> &#34;[FRESH]&#34;</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#24292E">        }</span></span>
<span class="line"><span style="color:#24292E">    }</span></span>
<span class="line"><span style="color:#6F42C1">    printf</span><span style="color:#24292E">(</span><span style="color:#032F62">&#34;===========================</span><span style="color:#005CC5">\n</span><span style="color:#032F62">&#34;</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#24292E">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49">int</span><span style="color:#6F42C1"> main</span><span style="color:#24292E">(</span><span style="color:#D73A49">int</span><span style="color:#E36209"> argc</span><span style="color:#24292E">, </span><span style="color:#D73A49">char</span><span style="color:#D73A49"> *</span><span style="color:#E36209">argv</span><span style="color:#D73A49">[]</span><span style="color:#24292E">)</span></span>
<span class="line"><span style="color:#24292E">{</span></span>
<span class="line"><span style="color:#D73A49">    const</span><span style="color:#D73A49"> int</span><span style="color:#24292E"> num_workers </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> 2</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#D73A49">    pid_t</span><span style="color:#E36209"> pids</span><span style="color:#24292E">[num_workers];</span></span>
<span class="line"><span style="color:#24292E">    </span></span>
<span class="line"><span style="color:#6F42C1">    printf</span><span style="color:#24292E">(</span><span style="color:#032F62">&#34;Creating shared key/value storage with anti-thundering herd...</span><span style="color:#005CC5">\n</span><span style="color:#032F62">&#34;</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#24292E">    </span></span>
<span class="line"><span style="color:#6A737D">    // Create shared memory segment</span></span>
<span class="line"><span style="color:#24292E">    storage </span><span style="color:#D73A49">=</span><span style="color:#6F42C1"> mmap</span><span style="color:#24292E">(</span><span style="color:#005CC5">NULL</span><span style="color:#24292E">, </span><span style="color:#D73A49">sizeof</span><span style="color:#24292E">(SharedStorage), </span></span>
<span class="line"><span style="color:#24292E">                   PROT_READ </span><span style="color:#D73A49">|</span><span style="color:#24292E"> PROT_WRITE, MAP_SHARED </span><span style="color:#D73A49">|</span><span style="color:#24292E"> MAP_ANONYMOUS, </span><span style="color:#D73A49">-</span><span style="color:#005CC5">1</span><span style="color:#24292E">, </span><span style="color:#005CC5">0</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#D73A49">    if</span><span style="color:#24292E"> (storage </span><span style="color:#D73A49">==</span><span style="color:#24292E"> MAP_FAILED) {</span></span>
<span class="line"><span style="color:#6F42C1">        perror</span><span style="color:#24292E">(</span><span style="color:#032F62">&#34;mmap failed&#34;</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#6F42C1">        exit</span><span style="color:#24292E">(</span><span style="color:#005CC5">1</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#24292E">    }</span></span>
<span class="line"><span style="color:#24292E">    </span></span>
<span class="line"><span style="color:#6A737D">    // Initialize shared storage</span></span>
<span class="line"><span style="color:#6F42C1">    memset</span><span style="color:#24292E">(storage, </span><span style="color:#005CC5">0</span><span style="color:#24292E">, </span><span style="color:#D73A49">sizeof</span><span style="color:#24292E">(SharedStorage));</span></span>
<span class="line"><span style="color:#24292E">    </span></span>
<span class="line"><span style="color:#D73A49">    pthread_mutexattr_t</span><span style="color:#24292E"> mutex_attr;</span></span>
<span class="line"><span style="color:#6F42C1">    pthread_mutexattr_init</span><span style="color:#24292E">(</span><span style="color:#D73A49">&amp;</span><span style="color:#24292E">mutex_attr);</span></span>
<span class="line"><span style="color:#6F42C1">    pthread_mutexattr_setpshared</span><span style="color:#24292E">(</span><span style="color:#D73A49">&amp;</span><span style="color:#24292E">mutex_attr, PTHREAD_PROCESS_SHARED);</span></span>
<span class="line"><span style="color:#6F42C1">    pthread_mutex_init</span><span style="color:#24292E">(</span><span style="color:#D73A49">&amp;</span><span style="color:#24292E">storage-&gt;global_mutex, </span><span style="color:#D73A49">&amp;</span><span style="color:#24292E">mutex_attr);</span></span>
<span class="line"><span style="color:#24292E">    </span></span>
<span class="line"><span style="color:#24292E">    storage-&gt;num_entries </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> 0</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#24292E">    </span></span>
<span class="line"><span style="color:#6F42C1">    printf</span><span style="color:#24292E">(</span><span style="color:#032F62">&#34;Spawning </span><span style="color:#005CC5">%d</span><span style="color:#032F62"> worker processes...</span><span style="color:#005CC5">\n</span><span style="color:#032F62">&#34;</span><span style="color:#24292E">, num_workers);</span></span>
<span class="line"><span style="color:#24292E">    </span></span>
<span class="line"><span style="color:#6A737D">    // Fork multiple worker processes</span></span>
<span class="line"><span style="color:#D73A49">    for</span><span style="color:#24292E"> (</span><span style="color:#D73A49">int</span><span style="color:#24292E"> i </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> 0</span><span style="color:#24292E">; i </span><span style="color:#D73A49">&lt;</span><span style="color:#24292E"> num_workers; i</span><span style="color:#D73A49">++</span><span style="color:#24292E">) {</span></span>
<span class="line"><span style="color:#D73A49">        pid_t</span><span style="color:#24292E"> pid </span><span style="color:#D73A49">=</span><span style="color:#6F42C1"> fork</span><span style="color:#24292E">();</span></span>
<span class="line"><span style="color:#D73A49">        if</span><span style="color:#24292E"> (pid </span><span style="color:#D73A49">==</span><span style="color:#005CC5"> 0</span><span style="color:#24292E">) {</span></span>
<span class="line"><span style="color:#6A737D">            // Child process</span></span>
<span class="line"><span style="color:#6F42C1">            srand</span><span style="color:#24292E">(</span><span style="color:#6F42C1">time</span><span style="color:#24292E">(</span><span style="color:#005CC5">NULL</span><span style="color:#24292E">) </span><span style="color:#D73A49">+</span><span style="color:#6F42C1"> getpid</span><span style="color:#24292E">());</span></span>
<span class="line"><span style="color:#6F42C1">            worker_process</span><span style="color:#24292E">(i </span><span style="color:#D73A49">+</span><span style="color:#005CC5"> 1</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#6F42C1">            exit</span><span style="color:#24292E">(</span><span style="color:#005CC5">0</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#24292E">        } </span><span style="color:#D73A49">else</span><span style="color:#D73A49"> if</span><span style="color:#24292E"> (pid </span><span style="color:#D73A49">&gt;</span><span style="color:#005CC5"> 0</span><span style="color:#24292E">) {</span></span>
<span class="line"><span style="color:#E36209">            pids</span><span style="color:#24292E">[i] </span><span style="color:#D73A49">=</span><span style="color:#24292E"> pid;</span></span>
<span class="line"><span style="color:#24292E">        } </span><span style="color:#D73A49">else</span><span style="color:#24292E"> {</span></span>
<span class="line"><span style="color:#6F42C1">            perror</span><span style="color:#24292E">(</span><span style="color:#032F62">&#34;fork failed&#34;</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#6F42C1">            exit</span><span style="color:#24292E">(</span><span style="color:#005CC5">1</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#24292E">        }</span></span>
<span class="line"><span style="color:#24292E">    }</span></span>
<span class="line"><span style="color:#24292E">    </span></span>
<span class="line"><span style="color:#6A737D">    // Wait for all workers to complete</span></span>
<span class="line"><span style="color:#D73A49">    for</span><span style="color:#24292E"> (</span><span style="color:#D73A49">int</span><span style="color:#24292E"> i </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> 0</span><span style="color:#24292E">; i </span><span style="color:#D73A49">&lt;</span><span style="color:#24292E"> num_workers; i</span><span style="color:#D73A49">++</span><span style="color:#24292E">) {</span></span>
<span class="line"><span style="color:#D73A49">        int</span><span style="color:#24292E"> status;</span></span>
<span class="line"><span style="color:#6F42C1">        waitpid</span><span style="color:#24292E">(</span><span style="color:#E36209">pids</span><span style="color:#24292E">[i], </span><span style="color:#D73A49">&amp;</span><span style="color:#24292E">status, </span><span style="color:#005CC5">0</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#24292E">    }</span></span>
<span class="line"><span style="color:#24292E">    </span></span>
<span class="line"><span style="color:#6F42C1">    print_storage_state</span><span style="color:#24292E">();</span></span>
<span class="line"><span style="color:#24292E">    </span></span>
<span class="line"><span style="color:#6A737D">    // Cleanup</span></span>
<span class="line"><span style="color:#D73A49">    for</span><span style="color:#24292E"> (</span><span style="color:#D73A49">int</span><span style="color:#24292E"> i </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> 0</span><span style="color:#24292E">; i </span><span style="color:#D73A49">&lt;</span><span style="color:#24292E"> storage-&gt;num_entries; i</span><span style="color:#D73A49">++</span><span style="color:#24292E">) {</span></span>
<span class="line"><span style="color:#6F42C1">        pthread_mutex_destroy</span><span style="color:#24292E">(</span><span style="color:#D73A49">&amp;</span><span style="color:#24292E">storage-&gt;entries[i].mutex);</span></span>
<span class="line"><span style="color:#6F42C1">        pthread_cond_destroy</span><span style="color:#24292E">(</span><span style="color:#D73A49">&amp;</span><span style="color:#24292E">storage-&gt;entries[i].init_cv);</span></span>
<span class="line"><span style="color:#24292E">    }</span></span>
<span class="line"><span style="color:#6F42C1">    pthread_mutex_destroy</span><span style="color:#24292E">(</span><span style="color:#D73A49">&amp;</span><span style="color:#24292E">storage-&gt;global_mutex);</span></span>
<span class="line"><span style="color:#6F42C1">    munmap</span><span style="color:#24292E">(storage, </span><span style="color:#D73A49">sizeof</span><span style="color:#24292E">(SharedStorage));</span></span>
<span class="line"><span style="color:#24292E">    </span></span>
<span class="line"><span style="color:#6F42C1">    printf</span><span style="color:#24292E">(</span><span style="color:#032F62">&#34;Demo completed successfully!</span><span style="color:#005CC5">\n</span><span style="color:#032F62">&#34;</span><span style="color:#24292E">);</span></span>
<span class="line"><span style="color:#D73A49">    return</span><span style="color:#005CC5"> 0</span><span style="color:#24292E">;</span></span>
<span class="line"><span style="color:#24292E">}</span></span></code></pre>]]></content:encoded>
    </item>
    <item>
      <title>Why I Often Choose Interfaces Over Inheritance</title>
      <link>https://kirshatrov.com/posts/interfaces-over-inheritance</link>
      <description></description>
      <guid>bbb3739d573e3efd2626de597cb3448d</guid>
      <pubDate>Tue, 01 Jul 2025 00:00:00 UTC</pubDate>
      <content:encoded><![CDATA[<p>From time to time I’ve been getting “Kir, why did you use interfaces here?” about Ruby and Python codebases I’ve worked on. It’s time to write a post that could serve as an extended GitHub comment with the answer “why”.</p>
<h2 id="the-problem-with-inheritance">The Problem with Inheritance</h2>
<p>Let’s use a real-life example of a <strong>repository object</strong>. We want to have a repository that has multiple entry points like <code>load{_multi}_by_{id,sku}</code>. This object should also provide a mock version to be used in tests.</p>
<p>Let’s start with implementing it using inheritance. I’m going to use Ruby, but the same reasoning applies to Python as well.</p>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="ruby"><code><span class="line"><span style="color:#D73A49">class</span><span style="color:#6F42C1"> BaseProductRepository</span></span>
<span class="line"><span style="color:#D73A49">  def</span><span style="color:#6F42C1"> load_by_id</span><span style="color:#24292E">(id)</span></span>
<span class="line"><span style="color:#D73A49">    raise</span><span style="color:#005CC5"> NotImplementedError</span></span>
<span class="line"><span style="color:#D73A49">  end</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49">  def</span><span style="color:#6F42C1"> load_by_sku</span><span style="color:#24292E">(sku)</span></span>
<span class="line"><span style="color:#D73A49">    raise</span><span style="color:#005CC5"> NotImplementedError</span></span>
<span class="line"><span style="color:#D73A49">  end</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49">  def</span><span style="color:#6F42C1"> load_multi_by_ids</span><span style="color:#24292E">(ids)</span></span>
<span class="line"><span style="color:#D73A49">    raise</span><span style="color:#005CC5"> NotImplementedError</span></span>
<span class="line"><span style="color:#D73A49">  end</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49">  def</span><span style="color:#6F42C1"> load_multi_by_skus</span><span style="color:#24292E">(skus)</span></span>
<span class="line"><span style="color:#D73A49">    raise</span><span style="color:#005CC5"> NotImplementedError</span></span>
<span class="line"><span style="color:#D73A49">  end</span></span>
<span class="line"><span style="color:#D73A49">end</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49">class</span><span style="color:#6F42C1"> ProductRepository</span><span style="color:#24292E"> &lt; </span><span style="color:#6F42C1">BaseProductRepository</span></span>
<span class="line"><span style="color:#D73A49">  def</span><span style="color:#6F42C1"> load_by_id</span><span style="color:#24292E">(id)</span></span>
<span class="line"><span style="color:#6A737D">    # implementation</span></span>
<span class="line"><span style="color:#D73A49">  end</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49">  def</span><span style="color:#6F42C1"> load_by_sku</span><span style="color:#24292E">(sku)</span></span>
<span class="line"><span style="color:#6A737D">    # implementation</span></span>
<span class="line"><span style="color:#D73A49">  end</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D">  # ... the rest of methods</span></span>
<span class="line"><span style="color:#D73A49">end</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49">class</span><span style="color:#6F42C1"> MockProductRepository</span><span style="color:#24292E"> &lt; </span><span style="color:#6F42C1">BaseProductRepository</span></span>
<span class="line"><span style="color:#D73A49">  def</span><span style="color:#6F42C1"> initialize</span><span style="color:#24292E">(products)</span></span>
<span class="line"><span style="color:#6A737D">    # takes a list of products and stores them in a hash</span></span>
<span class="line"><span style="color:#6A737D">    # ...</span></span>
<span class="line"><span style="color:#D73A49">  end</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49">  def</span><span style="color:#6F42C1"> load_by_id</span><span style="color:#24292E">(id)</span></span>
<span class="line"><span style="color:#6A737D">    # implementation</span></span>
<span class="line"><span style="color:#D73A49">  end</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49">  def</span><span style="color:#6F42C1"> load_by_sku</span><span style="color:#24292E">(sku)</span></span>
<span class="line"><span style="color:#6A737D">    # implementation</span></span>
<span class="line"><span style="color:#D73A49">  end</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D">  # ... the rest of methods</span></span>
<span class="line"><span style="color:#D73A49">end</span></span></code></pre>
<p>This repository evolves over time, and another developer adds <code>load_by_tenant_id_and_id</code> as a way to scope the loaded resource by a tenant ID to avoid security issues of accidentally loading another tenant’s product.</p>
<p>That developer might add <code>load_by_tenant_id_and_id</code> to <code>ProductRepository</code> or to <code>BaseProductRepository</code> depending on their taste and awareness. But then there’s nothing in place to check that <code>MockProductRepository</code> complies with the base class. You may only find out later when that new method gets called and <code>NotImplementedError</code> is raised. Surprise!</p>
<p>To some degree, you get zero value from <code>BaseProductRepository</code> here. You might as well just have <code>ProductRepository</code> and <code>MockProductRepository</code> not have any base class at all.</p>
<h2 id="the-power-of-interfaces">The Power of Interfaces</h2>
<p>I’m going to use <a href="https://sorbet.org/" rel="noreferrer" target="_blank">Sorbet</a> for this example, but what I’m pointing out is not Sorbet specific and could be implemented with RBS types if you don’t like Sorbet.</p>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="ruby"><code><span class="line"><span style="color:#D73A49">class</span><span style="color:#6F42C1"> BaseProductRepository</span></span>
<span class="line"><span style="color:#D73A49">  extend</span><span style="color:#005CC5"> T</span><span style="color:#24292E">::</span><span style="color:#005CC5">Sig</span></span>
<span class="line"><span style="color:#D73A49">  extend</span><span style="color:#005CC5"> T</span><span style="color:#24292E">::</span><span style="color:#005CC5">Helpers</span></span>
<span class="line"><span style="color:#24292E">  interface!</span></span>
<span class="line"></span>
<span class="line"><span style="color:#24292E">  sig { abstract.</span><span style="color:#6F42C1">params</span><span style="color:#24292E">(</span><span style="color:#005CC5">id:</span><span style="color:#005CC5"> Integer</span><span style="color:#24292E">).</span><span style="color:#6F42C1">returns</span><span style="color:#24292E">(</span><span style="color:#005CC5">Product</span><span style="color:#24292E">) }</span></span>
<span class="line"><span style="color:#D73A49">  def</span><span style="color:#6F42C1"> load_by_id</span><span style="color:#24292E">(id); </span><span style="color:#D73A49">end</span></span>
<span class="line"></span>
<span class="line"><span style="color:#24292E">  sig { abstract.</span><span style="color:#6F42C1">params</span><span style="color:#24292E">(</span><span style="color:#005CC5">sku:</span><span style="color:#005CC5"> String</span><span style="color:#24292E">).</span><span style="color:#6F42C1">returns</span><span style="color:#24292E">(</span><span style="color:#005CC5">Product</span><span style="color:#24292E">) }</span></span>
<span class="line"><span style="color:#D73A49">  def</span><span style="color:#6F42C1"> load_by_sku</span><span style="color:#24292E">(sku); </span><span style="color:#D73A49">end</span></span>
<span class="line"></span>
<span class="line"><span style="color:#24292E">  sig { abstract.</span><span style="color:#6F42C1">params</span><span style="color:#24292E">(</span><span style="color:#005CC5">ids:</span><span style="color:#005CC5"> T</span><span style="color:#24292E">::</span><span style="color:#005CC5">Array</span><span style="color:#24292E">[</span><span style="color:#005CC5">Integer</span><span style="color:#24292E">]).</span><span style="color:#6F42C1">returns</span><span style="color:#24292E">(</span><span style="color:#005CC5">T</span><span style="color:#24292E">::</span><span style="color:#005CC5">Array</span><span style="color:#24292E">[</span><span style="color:#005CC5">Product</span><span style="color:#24292E">]) }</span></span>
<span class="line"><span style="color:#D73A49">  def</span><span style="color:#6F42C1"> load_multi_by_ids</span><span style="color:#24292E">(ids); </span><span style="color:#D73A49">end</span></span>
<span class="line"></span>
<span class="line"><span style="color:#24292E">  sig { abstract.</span><span style="color:#6F42C1">params</span><span style="color:#24292E">(</span><span style="color:#005CC5">skus:</span><span style="color:#005CC5"> T</span><span style="color:#24292E">::</span><span style="color:#005CC5">Array</span><span style="color:#24292E">[</span><span style="color:#005CC5">String</span><span style="color:#24292E">]).</span><span style="color:#6F42C1">returns</span><span style="color:#24292E">(</span><span style="color:#005CC5">T</span><span style="color:#24292E">::</span><span style="color:#005CC5">Array</span><span style="color:#24292E">[</span><span style="color:#005CC5">Product</span><span style="color:#24292E">]) }</span></span>
<span class="line"><span style="color:#D73A49">  def</span><span style="color:#6F42C1"> load_multi_by_skus</span><span style="color:#24292E">(skus); </span><span style="color:#D73A49">end</span></span>
<span class="line"><span style="color:#D73A49">end</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49">class</span><span style="color:#6F42C1"> ProductRepository</span></span>
<span class="line"><span style="color:#D73A49">  extend</span><span style="color:#005CC5"> T</span><span style="color:#24292E">::</span><span style="color:#005CC5">Sig</span></span>
<span class="line"><span style="color:#D73A49">  include</span><span style="color:#005CC5"> BaseProductRepository</span></span>
<span class="line"></span>
<span class="line"><span style="color:#24292E">  sig { override.</span><span style="color:#6F42C1">params</span><span style="color:#24292E">(</span><span style="color:#005CC5">id:</span><span style="color:#005CC5"> Integer</span><span style="color:#24292E">).</span><span style="color:#6F42C1">returns</span><span style="color:#24292E">(</span><span style="color:#005CC5">Product</span><span style="color:#24292E">) }</span></span>
<span class="line"><span style="color:#D73A49">  def</span><span style="color:#6F42C1"> load_by_id</span><span style="color:#24292E">(id)</span></span>
<span class="line"><span style="color:#6A737D">    # implementation</span></span>
<span class="line"><span style="color:#D73A49">  end</span></span>
<span class="line"></span>
<span class="line"><span style="color:#24292E">  sig { override.</span><span style="color:#6F42C1">params</span><span style="color:#24292E">(</span><span style="color:#005CC5">sku:</span><span style="color:#005CC5"> String</span><span style="color:#24292E">).</span><span style="color:#6F42C1">returns</span><span style="color:#24292E">(</span><span style="color:#005CC5">Product</span><span style="color:#24292E">) }</span></span>
<span class="line"><span style="color:#D73A49">  def</span><span style="color:#6F42C1"> load_by_sku</span><span style="color:#24292E">(sku)</span></span>
<span class="line"><span style="color:#6A737D">    # implementation</span></span>
<span class="line"><span style="color:#D73A49">  end</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D">  # ... rest of methods</span></span>
<span class="line"><span style="color:#D73A49">end</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49">class</span><span style="color:#6F42C1"> MockProductRepository</span></span>
<span class="line"><span style="color:#D73A49">  extend</span><span style="color:#005CC5"> T</span><span style="color:#24292E">::</span><span style="color:#005CC5">Sig</span></span>
<span class="line"><span style="color:#D73A49">  include</span><span style="color:#005CC5"> BaseProductRepository</span></span>
<span class="line"><span style="color:#6A737D">  # ...</span></span>
<span class="line"><span style="color:#D73A49">end</span></span></code></pre>
<p>Now, if you dare to forget to implement some of those methods on <code>MockProductRepository</code>, the type check will not pass.</p>
<p>The same applies to Python via <code>Protocol</code> interfaces:</p>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="python"><code><span class="line"><span style="color:#D73A49">from</span><span style="color:#24292E"> typing </span><span style="color:#D73A49">import</span><span style="color:#24292E"> Protocol, List</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49">class</span><span style="color:#6F42C1"> BaseProductRepository</span><span style="color:#24292E">(</span><span style="color:#6F42C1">Protocol</span><span style="color:#24292E">):</span></span>
<span class="line"><span style="color:#D73A49">    def</span><span style="color:#6F42C1"> load_by_id</span><span style="color:#24292E">(self, id: </span><span style="color:#005CC5">int</span><span style="color:#24292E">) -&gt; Product:</span></span>
<span class="line"><span style="color:#005CC5">        ...</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49">    def</span><span style="color:#6F42C1"> load_by_sku</span><span style="color:#24292E">(self, sku: </span><span style="color:#005CC5">str</span><span style="color:#24292E">) -&gt; Product:</span></span>
<span class="line"><span style="color:#005CC5">        ...</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49">    def</span><span style="color:#6F42C1"> load_multi_by_ids</span><span style="color:#24292E">(self, ids: List[</span><span style="color:#005CC5">int</span><span style="color:#24292E">]) -&gt; List[Product]:</span></span>
<span class="line"><span style="color:#005CC5">        ...</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49">    def</span><span style="color:#6F42C1"> load_multi_by_skus</span><span style="color:#24292E">(self, skus: List[</span><span style="color:#005CC5">str</span><span style="color:#24292E">]) -&gt; List[Product]:</span></span>
<span class="line"><span style="color:#005CC5">        ...</span></span></code></pre>
<p>This leaves no way for an implementation of <code>BaseProductRepository</code> to forget some of those methods, especially when the interface evolves over time.</p>
<h2 id="bonus-organizing-interfaces-in-ruby">Bonus: Organizing Interfaces in Ruby</h2>
<p>I don’t necessarily love <code>Base</code> as a keyword, and the fact that it lives in the parent namespace.</p>
<p>I grew to like the approach that was introduced at Shopify by a team that was really into typing and happened to be an early Sorbet adopter. The approach goes like this:</p>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="ruby"><code><span class="line"><span style="color:#D73A49">module</span><span style="color:#6F42C1"> ProductRepository</span></span>
<span class="line"><span style="color:#24292E">  interface!</span></span>
<span class="line"></span>
<span class="line"><span style="color:#24292E">  sig { abstract.</span><span style="color:#6F42C1">params</span><span style="color:#24292E">(</span><span style="color:#005CC5">id:</span><span style="color:#005CC5"> Integer</span><span style="color:#24292E">).</span><span style="color:#6F42C1">returns</span><span style="color:#24292E">(</span><span style="color:#005CC5">Product</span><span style="color:#24292E">) }</span></span>
<span class="line"><span style="color:#D73A49">  def</span><span style="color:#6F42C1"> load_by_id</span><span style="color:#24292E">(id); </span><span style="color:#D73A49">end</span></span>
<span class="line"></span>
<span class="line"><span style="color:#24292E">  sig { abstract.</span><span style="color:#6F42C1">params</span><span style="color:#24292E">(</span><span style="color:#005CC5">sku:</span><span style="color:#005CC5"> String</span><span style="color:#24292E">).</span><span style="color:#6F42C1">returns</span><span style="color:#24292E">(</span><span style="color:#005CC5">Product</span><span style="color:#24292E">) }</span></span>
<span class="line"><span style="color:#D73A49">  def</span><span style="color:#6F42C1"> load_by_sku</span><span style="color:#24292E">(sku); </span><span style="color:#D73A49">end</span></span>
<span class="line"><span style="color:#24292E">  </span></span>
<span class="line"><span style="color:#6A737D">  # ...</span></span>
<span class="line"><span style="color:#24292E">  </span></span>
<span class="line"><span style="color:#D73A49">  class</span><span style="color:#6F42C1"> Impl</span></span>
<span class="line"><span style="color:#D73A49">    include</span><span style="color:#005CC5"> ProductRepository</span></span>
<span class="line"><span style="color:#24292E">    </span></span>
<span class="line"><span style="color:#D73A49">    def</span><span style="color:#6F42C1"> load_by_id</span><span style="color:#24292E">(id)</span></span>
<span class="line"><span style="color:#6A737D">      # ...</span></span>
<span class="line"><span style="color:#D73A49">    end</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49">    def</span><span style="color:#6F42C1"> load_by_sku</span><span style="color:#24292E">(sku)</span></span>
<span class="line"><span style="color:#6A737D">      # ...</span></span>
<span class="line"><span style="color:#D73A49">    end</span></span>
<span class="line"><span style="color:#D73A49">  end</span></span>
<span class="line"><span style="color:#D73A49">end</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D"># ProductRepository::Impl can be used anywhere in the codebase.</span></span>
<span class="line"><span style="color:#6A737D"># If needed, somewhere in test/support:</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49">class</span><span style="color:#6F42C1"> MockProductRepository</span></span>
<span class="line"><span style="color:#D73A49">  include</span><span style="color:#005CC5"> ProductRepository</span></span>
<span class="line"><span style="color:#6A737D">  # ...</span></span>
<span class="line"><span style="color:#D73A49">end</span></span></code></pre>
<p>This approach provides the benefit of the main implementation living in the same file as the interface, without having both <code>BaseProductRepository</code> and <code>ProductRepository</code> in the parent namespace.</p>]]></content:encoded>
    </item>
    <item>
      <title>Tenant-based routing and consistent hashing</title>
      <link>https://kirshatrov.com/posts/chash</link>
      <description></description>
      <guid>da2e5a76964dbd0bbfc588b4da61b893</guid>
      <pubDate>Sun, 01 Jun 2025 00:00:00 UTC</pubDate>
      <content:encoded><![CDATA[<p>Back in 2022 when I worked on <a href="https://github.com/kubernetes/ingress-nginx/pull/9239" rel="noreferrer" target="_blank">tenant-based</a> routing which implemented the <a href="https://arxiv.org/abs/1608.01350" rel="noreferrer" target="_blank">Consistent Hashing with Bounded Loads</a> paper, one of the parts of that project was the <strong>hash-ring consistent hashing</strong> algorithm. That algorithm stuck with me as it can be extremely useful in a bunch of other applications.</p>
<h2 id="the-challenge-of-tenant-based-routing">The Challenge of Tenant-Based Routing</h2>
<p>When you’re building a multi-tenant system, one of the core challenges is <strong>routing requests efficiently</strong>. Imagine you have a SaaS application serving thousands of tenants, and you want to distribute their workloads across multiple backend servers.</p>
<p>The naive approach might be to use round-robin or random distribution, but that doesn’t account for tenant locality. If tenant A’s requests are scattered across all servers, you lose benefits like:</p>
<ul>
<li><strong>Cache locality</strong> (tenant’s data stays warm in server memory)</li>
<li><strong>Connection pooling</strong> (database connections can be reused for the same tenant)</li>
<li><strong>Resource isolation</strong> (noisy tenants don’t affect others as much)</li>
</ul>
<p>What you really want is <strong>consistent tenant-to-server mapping</strong> that remains stable even when servers are added or removed. This is where consistent hashing algorithms shine.</p>
<h2 id="consistent-hashing-the-hash-ring-approach">Consistent Hashing: The Hash Ring Approach</h2>
<p>Here’s the <a href="https://gist.github.com/kirs/4e7d4d5aec9c6ade10f39f9eaab0bd87" rel="noreferrer" target="_blank">gist of it, implemented in C</a>. There’s also <a href="https://gist.github.com/kirs/534b07de1144c920392b132e81e4968a" rel="noreferrer" target="_blank">one in Go</a> to make it easier to read.</p>
<p><strong>Let’s look into why it is so powerful with a few code examples.</strong> I’m going to use Go implementation as an example here.</p>
<h3 id="how-much-data-really-moves-when-a-new-node-appears">How much data really moves when a new node appears?</h3>
<p>Below is a small, reproducible experiment that contrasts a <em>consistent-hash
ring</em> with the classic <em>hash-mod-N</em> sharding strategy.</p>
<p>We start with <strong>10</strong> nodes (<code>N0…N9</code>), then introduce an eleventh one
(<code>N10</code>) and count how many out of <strong>1 000 000</strong> keys have to migrate.</p>
<hr/>
<h4 id="consistent-hashing-160-virtual-points-per-node">Consistent hashing (160 virtual points per node)</h4>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="go"><code><span class="line"><span style="color:#6A737D">// Build a ring with ten nodes.</span></span>
<span class="line"><span style="color:#24292E">ring </span><span style="color:#D73A49">:=</span><span style="color:#24292E"> chash.</span><span style="color:#6F42C1">New</span><span style="color:#24292E">([]</span><span style="color:#D73A49">string</span><span style="color:#24292E">{</span></span>
<span class="line"><span style="color:#032F62">	&#34;N0&#34;</span><span style="color:#24292E">, </span><span style="color:#032F62">&#34;N1&#34;</span><span style="color:#24292E">, </span><span style="color:#032F62">&#34;N2&#34;</span><span style="color:#24292E">, </span><span style="color:#032F62">&#34;N3&#34;</span><span style="color:#24292E">, </span><span style="color:#032F62">&#34;N4&#34;</span><span style="color:#24292E">,</span></span>
<span class="line"><span style="color:#032F62">	&#34;N5&#34;</span><span style="color:#24292E">, </span><span style="color:#032F62">&#34;N6&#34;</span><span style="color:#24292E">, </span><span style="color:#032F62">&#34;N7&#34;</span><span style="color:#24292E">, </span><span style="color:#032F62">&#34;N8&#34;</span><span style="color:#24292E">, </span><span style="color:#032F62">&#34;N9&#34;</span><span style="color:#24292E">,</span></span>
<span class="line"><span style="color:#24292E">})</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D">// Remember the original owner of every key.</span></span>
<span class="line"><span style="color:#24292E">oldOwner </span><span style="color:#D73A49">:=</span><span style="color:#6F42C1"> make</span><span style="color:#24292E">([]</span><span style="color:#D73A49">string</span><span style="color:#24292E">, </span><span style="color:#005CC5">1_000_000</span><span style="color:#24292E">)</span></span>
<span class="line"><span style="color:#D73A49">for</span><span style="color:#24292E"> i </span><span style="color:#D73A49">:=</span><span style="color:#005CC5"> 0</span><span style="color:#24292E">; i </span><span style="color:#D73A49">&lt;</span><span style="color:#005CC5"> 1_000_000</span><span style="color:#24292E">; i</span><span style="color:#D73A49">++</span><span style="color:#24292E"> {</span></span>
<span class="line"><span style="color:#24292E">	oldOwner[i], _ </span><span style="color:#D73A49">=</span><span style="color:#24292E"> ring.</span><span style="color:#6F42C1">Find</span><span style="color:#24292E">([]</span><span style="color:#D73A49">byte</span><span style="color:#24292E">(fmt.</span><span style="color:#6F42C1">Sprint</span><span style="color:#24292E">(i)))</span></span>
<span class="line"><span style="color:#24292E">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D">// Add the new node.</span></span>
<span class="line"><span style="color:#24292E">ring.</span><span style="color:#6F42C1">Add</span><span style="color:#24292E">(</span><span style="color:#032F62">&#34;N10&#34;</span><span style="color:#24292E">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D">// Count how many keys changed owner.</span></span>
<span class="line"><span style="color:#24292E">moved </span><span style="color:#D73A49">:=</span><span style="color:#005CC5"> 0</span></span>
<span class="line"><span style="color:#D73A49">for</span><span style="color:#24292E"> i </span><span style="color:#D73A49">:=</span><span style="color:#005CC5"> 0</span><span style="color:#24292E">; i </span><span style="color:#D73A49">&lt;</span><span style="color:#005CC5"> 1_000_000</span><span style="color:#24292E">; i</span><span style="color:#D73A49">++</span><span style="color:#24292E"> {</span></span>
<span class="line"><span style="color:#24292E">	newID, _ </span><span style="color:#D73A49">:=</span><span style="color:#24292E"> ring.</span><span style="color:#6F42C1">Find</span><span style="color:#24292E">([]</span><span style="color:#D73A49">byte</span><span style="color:#24292E">(fmt.</span><span style="color:#6F42C1">Sprint</span><span style="color:#24292E">(i)))</span></span>
<span class="line"><span style="color:#D73A49">	if</span><span style="color:#24292E"> newID </span><span style="color:#D73A49">!=</span><span style="color:#24292E"> oldOwner[i] {</span></span>
<span class="line"><span style="color:#24292E">		moved</span><span style="color:#D73A49">++</span></span>
<span class="line"><span style="color:#24292E">	}</span></span>
<span class="line"><span style="color:#24292E">}</span></span>
<span class="line"><span style="color:#24292E">fmt.</span><span style="color:#6F42C1">Printf</span><span style="color:#24292E">(</span><span style="color:#032F62">&#34;consistent-hash: </span><span style="color:#005CC5">%.2f</span><span style="color:#005CC5"> %%</span><span style="color:#032F62"> of keys moved</span><span style="color:#005CC5">\n</span><span style="color:#032F62">&#34;</span><span style="color:#24292E">,</span></span>
<span class="line"><span style="color:#005CC5">	100</span><span style="color:#D73A49">*float64</span><span style="color:#24292E">(moved)</span><span style="color:#D73A49">/</span><span style="color:#005CC5">1_000_000</span><span style="color:#24292E">)</span></span></code></pre>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="plaintext"><code><span class="line"><span>consistent-hash: 9.07 % of keys moved     // ≈ 1⁄11</span></span></code></pre>
<p>Only the slice of the ring that now belongs to N10 (roughly one-eleventh) is re-assigned. The remaining 90% of keys stay exactly where they were.</p>
<h4 id="naive-mod-n-sharding">Naive mod-N sharding</h4>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="go"><code><span class="line"><span style="color:#24292E">oldOwner </span><span style="color:#D73A49">:=</span><span style="color:#6F42C1"> make</span><span style="color:#24292E">([]</span><span style="color:#D73A49">int</span><span style="color:#24292E">, </span><span style="color:#005CC5">1_000_000</span><span style="color:#24292E">)</span></span>
<span class="line"><span style="color:#D73A49">for</span><span style="color:#24292E"> i </span><span style="color:#D73A49">:=</span><span style="color:#005CC5"> 0</span><span style="color:#24292E">; i </span><span style="color:#D73A49">&lt;</span><span style="color:#005CC5"> 1_000_000</span><span style="color:#24292E">; i</span><span style="color:#D73A49">++</span><span style="color:#24292E"> {</span></span>
<span class="line"><span style="color:#24292E">	oldOwner[i] </span><span style="color:#D73A49">=</span><span style="color:#24292E"> crc32.</span><span style="color:#6F42C1">ChecksumIEEE</span><span style="color:#24292E">([]</span><span style="color:#D73A49">byte</span><span style="color:#24292E">(fmt.</span><span style="color:#6F42C1">Sprint</span><span style="color:#24292E">(i))) </span><span style="color:#D73A49">%</span><span style="color:#005CC5"> 10</span></span>
<span class="line"><span style="color:#24292E">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#24292E">moved </span><span style="color:#D73A49">:=</span><span style="color:#005CC5"> 0</span></span>
<span class="line"><span style="color:#D73A49">for</span><span style="color:#24292E"> i </span><span style="color:#D73A49">:=</span><span style="color:#005CC5"> 0</span><span style="color:#24292E">; i </span><span style="color:#D73A49">&lt;</span><span style="color:#005CC5"> 1_000_000</span><span style="color:#24292E">; i</span><span style="color:#D73A49">++</span><span style="color:#24292E"> {</span></span>
<span class="line"><span style="color:#24292E">	newOwner </span><span style="color:#D73A49">:=</span><span style="color:#24292E"> crc32.</span><span style="color:#6F42C1">ChecksumIEEE</span><span style="color:#24292E">([]</span><span style="color:#D73A49">byte</span><span style="color:#24292E">(fmt.</span><span style="color:#6F42C1">Sprint</span><span style="color:#24292E">(i))) </span><span style="color:#D73A49">%</span><span style="color:#005CC5"> 11</span></span>
<span class="line"><span style="color:#D73A49">	if</span><span style="color:#24292E"> newOwner </span><span style="color:#D73A49">!=</span><span style="color:#24292E"> oldOwner[i] {</span></span>
<span class="line"><span style="color:#24292E">		moved</span><span style="color:#D73A49">++</span></span>
<span class="line"><span style="color:#24292E">	}</span></span>
<span class="line"><span style="color:#24292E">}</span></span>
<span class="line"><span style="color:#24292E">fmt.</span><span style="color:#6F42C1">Printf</span><span style="color:#24292E">(</span><span style="color:#032F62">&#34;mod-sharding: </span><span style="color:#005CC5">%.2f</span><span style="color:#005CC5"> %%</span><span style="color:#032F62"> of keys moved</span><span style="color:#005CC5">\n</span><span style="color:#032F62">&#34;</span><span style="color:#24292E">,</span></span>
<span class="line"><span style="color:#005CC5">	100</span><span style="color:#D73A49">*float64</span><span style="color:#24292E">(moved)</span><span style="color:#D73A49">/</span><span style="color:#005CC5">1_000_000</span><span style="color:#24292E">)</span></span></code></pre>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="plaintext"><code><span class="line"><span>mod-sharding: 90.92 % of keys moved     // ≈ 10⁄11</span></span></code></pre>
<p>Which happens once every 11 hashes — so about 91% of the dataset is reshuffled.</p>
<h2 id="rendezvous-hashing-an-alternative-approach">Rendezvous Hashing: An Alternative Approach</h2>
<p>While consistent hashing with a hash ring is popular, there’s another elegant algorithm called <strong>Rendezvous hashing</strong> (also known as <strong>Highest Random Weight</strong> hashing). Instead of placing nodes on a ring, it assigns each key to the node that produces the highest hash value when combined with that key.</p>
<p>Here’s how it works:</p>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="go"><code><span class="line"><span style="color:#D73A49">type</span><span style="color:#6F42C1"> RendezvousHash</span><span style="color:#D73A49"> struct</span><span style="color:#24292E"> {</span></span>
<span class="line"><span style="color:#24292E">	nodes []</span><span style="color:#D73A49">string</span></span>
<span class="line"><span style="color:#24292E">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49">func</span><span style="color:#6F42C1"> NewRendezvous</span><span style="color:#24292E">(</span><span style="color:#E36209">nodes</span><span style="color:#24292E"> []</span><span style="color:#D73A49">string</span><span style="color:#24292E">) </span><span style="color:#D73A49">*</span><span style="color:#6F42C1">RendezvousHash</span><span style="color:#24292E"> {</span></span>
<span class="line"><span style="color:#D73A49">	return</span><span style="color:#D73A49"> &amp;</span><span style="color:#6F42C1">RendezvousHash</span><span style="color:#24292E">{nodes: </span><span style="color:#6F42C1">append</span><span style="color:#24292E">([]</span><span style="color:#D73A49">string</span><span style="color:#24292E">{}, nodes</span><span style="color:#D73A49">...</span><span style="color:#24292E">)}</span></span>
<span class="line"><span style="color:#24292E">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49">func</span><span style="color:#24292E"> (</span><span style="color:#E36209">r </span><span style="color:#D73A49">*</span><span style="color:#6F42C1">RendezvousHash</span><span style="color:#24292E">) </span><span style="color:#6F42C1">Find</span><span style="color:#24292E">(</span><span style="color:#E36209">key</span><span style="color:#24292E"> []</span><span style="color:#D73A49">byte</span><span style="color:#24292E">) </span><span style="color:#D73A49">string</span><span style="color:#24292E"> {</span></span>
<span class="line"><span style="color:#D73A49">	var</span><span style="color:#24292E"> maxNode </span><span style="color:#D73A49">string</span></span>
<span class="line"><span style="color:#D73A49">	var</span><span style="color:#24292E"> maxHash </span><span style="color:#D73A49">uint32</span></span>
<span class="line"><span style="color:#24292E">	</span></span>
<span class="line"><span style="color:#24292E">	keyStr </span><span style="color:#D73A49">:=</span><span style="color:#D73A49"> string</span><span style="color:#24292E">(key)</span></span>
<span class="line"><span style="color:#D73A49">	for</span><span style="color:#24292E"> _, node </span><span style="color:#D73A49">:=</span><span style="color:#D73A49"> range</span><span style="color:#24292E"> r.nodes {</span></span>
<span class="line"><span style="color:#6A737D">		// Combine key with node name and hash</span></span>
<span class="line"><span style="color:#24292E">		combined </span><span style="color:#D73A49">:=</span><span style="color:#24292E"> keyStr </span><span style="color:#D73A49">+</span><span style="color:#032F62"> &#34;:&#34;</span><span style="color:#D73A49"> +</span><span style="color:#24292E"> node</span></span>
<span class="line"><span style="color:#24292E">		hash </span><span style="color:#D73A49">:=</span><span style="color:#24292E"> crc32.</span><span style="color:#6F42C1">ChecksumIEEE</span><span style="color:#24292E">([]</span><span style="color:#D73A49">byte</span><span style="color:#24292E">(combined))</span></span>
<span class="line"><span style="color:#24292E">		</span></span>
<span class="line"><span style="color:#D73A49">		if</span><span style="color:#24292E"> hash </span><span style="color:#D73A49">&gt;</span><span style="color:#24292E"> maxHash {</span></span>
<span class="line"><span style="color:#24292E">			maxHash </span><span style="color:#D73A49">=</span><span style="color:#24292E"> hash</span></span>
<span class="line"><span style="color:#24292E">			maxNode </span><span style="color:#D73A49">=</span><span style="color:#24292E"> node</span></span>
<span class="line"><span style="color:#24292E">		}</span></span>
<span class="line"><span style="color:#24292E">	}</span></span>
<span class="line"><span style="color:#D73A49">	return</span><span style="color:#24292E"> maxNode</span></span>
<span class="line"><span style="color:#24292E">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49">func</span><span style="color:#24292E"> (</span><span style="color:#E36209">r </span><span style="color:#D73A49">*</span><span style="color:#6F42C1">RendezvousHash</span><span style="color:#24292E">) </span><span style="color:#6F42C1">Add</span><span style="color:#24292E">(</span><span style="color:#E36209">node</span><span style="color:#D73A49"> string</span><span style="color:#24292E">) {</span></span>
<span class="line"><span style="color:#24292E">	r.nodes </span><span style="color:#D73A49">=</span><span style="color:#6F42C1"> append</span><span style="color:#24292E">(r.nodes, node)</span></span>
<span class="line"><span style="color:#24292E">}</span></span></code></pre>
<p>Let’s run the same experiment with Rendezvous hashing:</p>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="go"><code><span class="line"><span style="color:#6A737D">// Build rendezvous hash with ten nodes.</span></span>
<span class="line"><span style="color:#24292E">rhash </span><span style="color:#D73A49">:=</span><span style="color:#6F42C1"> NewRendezvous</span><span style="color:#24292E">([]</span><span style="color:#D73A49">string</span><span style="color:#24292E">{</span></span>
<span class="line"><span style="color:#032F62">	&#34;N0&#34;</span><span style="color:#24292E">, </span><span style="color:#032F62">&#34;N1&#34;</span><span style="color:#24292E">, </span><span style="color:#032F62">&#34;N2&#34;</span><span style="color:#24292E">, </span><span style="color:#032F62">&#34;N3&#34;</span><span style="color:#24292E">, </span><span style="color:#032F62">&#34;N4&#34;</span><span style="color:#24292E">,</span></span>
<span class="line"><span style="color:#032F62">	&#34;N5&#34;</span><span style="color:#24292E">, </span><span style="color:#032F62">&#34;N6&#34;</span><span style="color:#24292E">, </span><span style="color:#032F62">&#34;N7&#34;</span><span style="color:#24292E">, </span><span style="color:#032F62">&#34;N8&#34;</span><span style="color:#24292E">, </span><span style="color:#032F62">&#34;N9&#34;</span><span style="color:#24292E">,</span></span>
<span class="line"><span style="color:#24292E">})</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D">// Remember the original owner of every key.</span></span>
<span class="line"><span style="color:#24292E">oldOwner </span><span style="color:#D73A49">:=</span><span style="color:#6F42C1"> make</span><span style="color:#24292E">([]</span><span style="color:#D73A49">string</span><span style="color:#24292E">, </span><span style="color:#005CC5">1_000_000</span><span style="color:#24292E">)</span></span>
<span class="line"><span style="color:#D73A49">for</span><span style="color:#24292E"> i </span><span style="color:#D73A49">:=</span><span style="color:#005CC5"> 0</span><span style="color:#24292E">; i </span><span style="color:#D73A49">&lt;</span><span style="color:#005CC5"> 1_000_000</span><span style="color:#24292E">; i</span><span style="color:#D73A49">++</span><span style="color:#24292E"> {</span></span>
<span class="line"><span style="color:#24292E">	oldOwner[i] </span><span style="color:#D73A49">=</span><span style="color:#24292E"> rhash.</span><span style="color:#6F42C1">Find</span><span style="color:#24292E">([]</span><span style="color:#D73A49">byte</span><span style="color:#24292E">(fmt.</span><span style="color:#6F42C1">Sprint</span><span style="color:#24292E">(i)))</span></span>
<span class="line"><span style="color:#24292E">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D">// Add the new node.</span></span>
<span class="line"><span style="color:#24292E">rhash.</span><span style="color:#6F42C1">Add</span><span style="color:#24292E">(</span><span style="color:#032F62">&#34;N10&#34;</span><span style="color:#24292E">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D">// Count how many keys changed owner.</span></span>
<span class="line"><span style="color:#24292E">moved </span><span style="color:#D73A49">:=</span><span style="color:#005CC5"> 0</span></span>
<span class="line"><span style="color:#D73A49">for</span><span style="color:#24292E"> i </span><span style="color:#D73A49">:=</span><span style="color:#005CC5"> 0</span><span style="color:#24292E">; i </span><span style="color:#D73A49">&lt;</span><span style="color:#005CC5"> 1_000_000</span><span style="color:#24292E">; i</span><span style="color:#D73A49">++</span><span style="color:#24292E"> {</span></span>
<span class="line"><span style="color:#24292E">	newOwner </span><span style="color:#D73A49">:=</span><span style="color:#24292E"> rhash.</span><span style="color:#6F42C1">Find</span><span style="color:#24292E">([]</span><span style="color:#D73A49">byte</span><span style="color:#24292E">(fmt.</span><span style="color:#6F42C1">Sprint</span><span style="color:#24292E">(i)))</span></span>
<span class="line"><span style="color:#D73A49">	if</span><span style="color:#24292E"> newOwner </span><span style="color:#D73A49">!=</span><span style="color:#24292E"> oldOwner[i] {</span></span>
<span class="line"><span style="color:#24292E">		moved</span><span style="color:#D73A49">++</span></span>
<span class="line"><span style="color:#24292E">	}</span></span>
<span class="line"><span style="color:#24292E">}</span></span>
<span class="line"><span style="color:#24292E">fmt.</span><span style="color:#6F42C1">Printf</span><span style="color:#24292E">(</span><span style="color:#032F62">&#34;rendezvous-hash: </span><span style="color:#005CC5">%.2f</span><span style="color:#005CC5"> %%</span><span style="color:#032F62"> of keys moved</span><span style="color:#005CC5">\n</span><span style="color:#032F62">&#34;</span><span style="color:#24292E">,</span></span>
<span class="line"><span style="color:#005CC5">	100</span><span style="color:#D73A49">*float64</span><span style="color:#24292E">(moved)</span><span style="color:#D73A49">/</span><span style="color:#005CC5">1_000_000</span><span style="color:#24292E">)</span></span></code></pre>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="plaintext"><code><span class="line"><span>rendezvous-hash: 9.09 % of keys moved     // ≈ 1⁄11</span></span></code></pre>
<p>Remarkably similar results! Rendezvous hashing also minimizes key movement when nodes are added or removed.</p>
<h3 id="which-one-should-you-pick">Which one should you pick?</h3>
<p><strong>Consistent hashing</strong> with a hash ring is what you’ll find in most production systems — Redis Cluster, Cassandra, and Amazon’s DynamoDB all use variations of it. The reason is lookup performance: with enough virtual nodes, you can find a key’s owner in O(log n) time using binary search. If you’re building something that needs to handle thousands of requests per second with many nodes, this matters.</p>
<p><strong>Rendezvous hashing</strong> is conceptually simpler and the code is easier to reason about, but every lookup requires checking all nodes (O(n)). That said, if you have a dozen backend servers and want dead-simple logic that just works, Rendezvous can be perfect.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Both consistent hashing and Rendezvous hashing solve the same fundamental problem: <strong>minimizing disruption when nodes are added or removed from a distributed system</strong>. This property makes them ideal for load balancers, distributed caches and CDNs.</p>
<p>I would love to write another post about the end-to-end implementation of tenant-based routing and why it can be extremely effective for your web apps.</p>]]></content:encoded>
    </item>
    <item>
      <title>Reverse engineering Claude Code</title>
      <link>https://kirshatrov.com/posts/claude-code-internals</link>
      <description></description>
      <guid>deeb0502756a721dd1672c057acc8921</guid>
      <pubDate>Tue, 01 Apr 2025 00:00:00 UTC</pubDate>
      <content:encoded><![CDATA[<p>I’ve been curious about what happens inside <a href="https://docs.anthropic.com/en/docs/agents-and-tools/claude-code/overview" rel="noreferrer" target="_blank">Claude Code</a> so I’ve spent a couple hours digging through prompts that it sends back to Anthropic.</p>
<p>As I’ve been going through that, I’ve gathered some insights why Claude Code is often slower and more expensive than other tools like Cursor.</p>
<div class="upd">
  <strong>Updated: </strong> Since this post was published, someone on the internet created much deeper analysis of Claude Code. <a href="https://southbridge-research.notion.site/claude-code-an-agentic-cleanroom-analysis" rel="noreferrer" target="_blank">Check it out!</a>
</div>
<h2 id="capturing-prompts">Capturing prompts</h2>
<p>It turned out a lot easier than I thought, thanks to <a href="https://mitmproxy.org/" rel="noreferrer" target="_blank">mitmproxy</a>:</p>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="plaintext"><code><span class="line"><span>$ brew install mitmproxy</span></span>
<span class="line"><span>$ mitmweb --mode reverse:https://api.anthropic.com --listen-port 8000</span></span>
<span class="line"><span>$ ANTHROPIC_BASE_URL=http://localhost:8000/ claude</span></span></code></pre>
<h2 id="getting-started">Getting started</h2>
<p>I opened a <a href="https://github.com/kirs/kirshatrov-com" rel="noreferrer" target="_blank">repo</a> with my personal blog and entered “describe what’s in this project”.</p>
<p>The very first thing you see is that Claude Code tries to figure out whether what you have entered is a continuation of the previous conversation or a new topic.</p>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="plaintext"><code><span class="line"><span>Analyze if this message indicates a new conversation topic.</span></span>
<span class="line"><span>If it does, extract a 2-3 word title that captures the new topic.</span></span>
<span class="line"><span>Format your response as a JSON object with two fields: &#39;isNewTopic&#39; (boolean) and &#39;title&#39; (string, or null if isNewTopic is false). Only include these fields, no other text.</span></span></code></pre>
<p>Once it determines that, it wraps all your inputs into this system prompt:</p>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="plaintext"><code><span class="line"><span>You are an agent for Claude Code, Anthropic&#39;s official CLI for Claude. Given the user&#39;s prompt, you should use the tools available to you to answer the user&#39;s question.</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Notes:</span></span>
<span class="line"><span>1. IMPORTANT: You should be concise, direct, and to the point, since your responses will be displayed on a command line interface. Answer the user&#39;s question directly, without elaboration, explanation, or details. One word answers are best. Avoid introductions, conclusions, and explanations. You MUST avoid text before/after your response, such as \&#34;The answer is &lt;answer&gt;.\&#34;, \&#34;Here is the content of the file...\&#34; or \&#34;Based on the information provided, the answer is...\&#34; or \&#34;Here is what I will do next...\&#34;.</span></span>
<span class="line"><span>2. When relevant, share file names and code snippets relevant to the query</span></span>
<span class="line"><span>3. Any file paths you return in your final response MUST be absolute. DO NOT use relative paths.</span></span>
<span class="line"><span>Here is useful information about the environment you are running in:</span></span>
<span class="line"><span>&lt;env&gt;</span></span>
<span class="line"><span>Working directory: /Users/kir/src/github.com/kirs/kirshatrov-com</span></span>
<span class="line"><span>Is directory a git repo: Yes</span></span>
<span class="line"><span>Platform: macos</span></span>
<span class="line"><span>Today&#39;s date: 2025-04-13</span></span>
<span class="line"><span>Model: claude-3-7-sonnet-20250219</span></span>
<span class="line"><span>&lt;/env&gt;</span></span>
<span class="line"><span></span></span>
<span class="line"><span>As you answer the user&#39;s questions, you can use the following context:</span></span>
<span class="line"><span></span></span>
<span class="line"><span>&lt;context name=&#34;directoryStructure&#34;&gt;Below is a snapshot of this project&#39;s file structure at the start of the conversation. This snapshot will NOT update during the conversation. It skips over .gitignore patterns.</span></span>
<span class="line"><span></span></span>
<span class="line"><span>- /Users/kir/src/github.com/kirs/kirshatrov-com/</span></span>
<span class="line"><span>  - README.md</span></span>
<span class="line"><span>  - _posts/</span></span>
<span class="line"><span>    - 2015-03-02-capistrano-unicorn-bundler.markdown</span></span>
<span class="line"><span>    - 2015-04-07-rails-5.markdown</span></span>
<span class="line"><span> ....</span></span>
<span class="line"><span>    - 2024-12-14-facebook-mysql-fork.md</span></span>
<span class="line"><span>  - bin/</span></span>
<span class="line"><span>...</span></span>
<span class="line"><span>&lt;/context&gt;</span></span>
<span class="line"><span>&lt;context name=&#34;gitStatus&#34;&gt;This is the git status at the start of the conversation. Note that this status is a snapshot in time, and will not update during the conversation.</span></span>
<span class="line"><span>Current branch: master</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Main branch (you will usually use this for PRs): master</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Status:</span></span>
<span class="line"><span>M .gitignore</span></span>
<span class="line"><span> D .ruby-version</span></span>
<span class="line"><span> M package-lock.json</span></span>
<span class="line"><span>?? playground/</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Recent commits:</span></span>
<span class="line"><span>58ce3f9 m4 --force</span></span>
<span class="line"><span>3b1052a correction</span></span>
<span class="line"><span>82fe70a fix stuff</span></span>
<span class="line"><span>cfd73d9 follow-up</span></span>
<span class="line"><span>&lt;/context&gt;</span></span></code></pre>
<p>It’s nice that the state of git is included into the context.
You can also notice that the context includes full tree directory view which can get somewhat big.</p>
<p>Part of this system prompt is a list of 11 tools that are available for the LLM to call:</p>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="plaintext"><code><span class="line"><span>dispatch_agent</span></span>
<span class="line"><span>Launch a new agent that has access to the following tools: View, GlobTool, GrepTool, LS, ReadNotebook, WebFetchTool. When you are searching for a keyword or file and are not confident that you will find the right match in the first few tries, use the Agent tool to perform the search for you.</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Bash</span></span>
<span class="line"><span>Executes a given bash command in a persistent shell session with optional timeout, ensuring proper handling and security measures. Includes specialized functionality for git operations like commits and pull requests.</span></span>
<span class="line"><span></span></span>
<span class="line"><span>BatchTool</span></span>
<span class="line"><span>Batch execution tool that runs multiple tool invocations in a single request. Tools are executed in parallel when possible, and otherwise serially. Takes a list of tool invocations (tool_name and input pairs) and returns the collected results from all invocations.</span></span>
<span class="line"><span></span></span>
<span class="line"><span>GlobTool</span></span>
<span class="line"><span>Fast file pattern matching tool that works with any codebase size. Supports glob patterns like &#34;/*.js&#34; or &#34;src//*.ts&#34;. Returns matching file paths sorted by modification time.</span></span>
<span class="line"><span>GrepTool</span></span>
<span class="line"><span>Fast content search tool that works with any codebase size. Searches file contents using regular expressions. Supports full regex syntax and filters files by pattern with the include parameter.</span></span>
<span class="line"><span></span></span>
<span class="line"><span>LS</span></span>
<span class="line"><span>Lists files and directories in a given path. The path parameter must be an absolute path, not a relative path. You can optionally provide an array of glob patterns to ignore.</span></span>
<span class="line"><span></span></span>
<span class="line"><span>View</span></span>
<span class="line"><span>Reads a file from the local filesystem. The file_path parameter must be an absolute path. By default, reads up to 2000 lines starting from the beginning of the file.</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Edit</span></span>
<span class="line"><span>Tool for editing files. For moving or renaming files, use the Bash tool with the &#39;mv&#39; command instead. For larger edits, use the Write tool to overwrite files.</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Write</span></span>
<span class="line"><span>Write a file to the local filesystem. Overwrites the existing file if there is one.</span></span>
<span class="line"><span>Before using this tool:</span></span>
<span class="line"><span>  1. Use the ReadFile tool to understand the file&#39;s contents and context</span></span>
<span class="line"><span>  2. Directory Verification (only applicable when creating new files):</span></span>
<span class="line"><span>     - Use the LS tool to verify the parent directory exists and is the correct location&#34;</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Replace</span></span>
<span class="line"><span>Write a file to the local filesystem. Overwrites the existing file if there is one.</span></span>
<span class="line"><span></span></span>
<span class="line"><span>ReadNotebook</span></span>
<span class="line"><span>Reads a Jupyter notebook (.ipynb file) and returns all of the cells with their outputs.</span></span>
<span class="line"><span></span></span>
<span class="line"><span>NotebookEditCell</span></span>
<span class="line"><span>Completely replaces the contents of a specific cell in a Jupyter notebook (.ipynb file) with new source.</span></span>
<span class="line"><span></span></span>
<span class="line"><span>WebFetchTool</span></span>
<span class="line"><span>Fetches content from a specified URL and processes it using an AI model. Takes a URL and a prompt as input, fetches the URL content, converts HTML to markdown, and processes the content.</span></span></code></pre>
<p>My original input (<code>“describe what’s in this project”</code>) then gets transformed into the <code>dispatch_agent</code> tool call with this instruction:</p>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="plaintext"><code><span class="line"><span>Analyze the repository structure and determine what kind of project this is.</span></span>
<span class="line"><span>Look at the file structure, package.json, and any configuration files to understand the tech stack,</span></span>
<span class="line"><span>then provide a concise summary of what this project appears to be.</span></span></code></pre>
<p>This is then transformed into a few <code>view</code> tool calls for those files:</p>
<ul>
<li><code>README.md</code></li>
<li><code>package.json</code></li>
<li><code>pages/index.js</code></li>
<li><code>pages/post.js</code></li>
<li><code>pages/travels.js</code></li>
</ul>
<p>The result of feeding those files back to an LLM makes it into the following summary:</p>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="plaintext"><code><span class="line"><span>This is Kir Shatrov&#39;s personal website/blog built with Next.js, React, and Tailwind CSS. It includes:</span></span>
<span class="line"><span></span></span>
<span class="line"><span>- Blog posts on tech topics (Ruby/Rails, databases, engineering leadership)</span></span>
<span class="line"><span>- Photography gallery</span></span>
<span class="line"><span>- Travel map/history</span></span>
<span class="line"><span>- Speaking engagements</span></span>
<span class="line"><span>- Fabrication projects (woodworking/making)</span></span>
<span class="line"><span></span></span>
<span class="line"><span>The site uses markdown for blog content with posts dating back to 2015, following standard Next.js project structure.</span></span></code></pre>
<p>The above took 40 seconds of LLM time and cost me $0.11.</p>
<img width="896" height="242" src="https://kirshatrov.com/assets/post-images/2025/claude-describe.png"/>
<h2 id="writing-new-code">Writing new code</h2>
<p>My next test input was:</p>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="plaintext"><code><span class="line"><span>Please write a bash script which displays the top story on Hacker News.</span></span>
<span class="line"><span>It should show the title, the link, and the number of points.</span></span></code></pre>
<p>That spawned an agent use that has attempted to read <code>https://github.com/HackerNews/API</code>. But Claude’s self-limiting security policy has stopped that:</p>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="plaintext"><code><span class="line"><span>Error: Cannot fetch URL &#34;https://github.com/HackerNews/API&#34;. For security, you can only fetch URLs from hosts that the user has mentioned in their messages or that are found in CLAUDE.md files or project files.</span></span></code></pre>
<p>Then it tried to read <code>https://hackernews.api-docs.io</code> and run into the same error.</p>
<p>Then it tried to read <code>https://firebase.google.com/docs/database</code> and the same thing happened.</p>
<p>It then changed the strategy and outputted this:</p>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="plaintext"><code><span class="line"><span>I apologize for the limitations in accessing external URLs. Due to security restrictions, I can only fetch URLs that have been mentioned by you or are in project files.</span></span>
<span class="line"><span></span></span>
<span class="line"><span>The Hacker News API is hosted by Firebase and provides several endpoints, including:</span></span>
<span class="line"><span></span></span>
<span class="line"><span>- `https://hacker-news.firebaseio.com/v0/topstories.json` - Returns IDs of top stories</span></span>
<span class="line"><span>- `https://hacker-news.firebaseio.com/v0/item/{id}.json` - Returns details of a specific item</span></span>
<span class="line"><span></span></span>
<span class="line"><span>To create a bash script to fetch the top story, you would:</span></span>
<span class="line"><span>1. Fetch the array of top story IDs</span></span>
<span class="line"><span>2. Get the first ID from that array</span></span>
<span class="line"><span>3. Fetch the details for that ID</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Here&#39;s a basic bash script example:</span></span>
<span class="line"><span></span></span>
<span class="line"><span>#!/bin/bash</span></span>
<span class="line"><span>...</span></span>
<span class="line"><span></span></span></code></pre>
<p>What’s interesting is that none of that got saved or executed. So I’ve typed <code>save that and execute</code>.</p>
<p>Now, remember this prompt from the beginning?</p>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="plaintext"><code><span class="line"><span>Analyze if this message indicates a new conversation topic.</span></span>
<span class="line"><span>If it does, extract a 2-3 word title that captures the new topic.</span></span>
<span class="line"><span>Format your response as a JSON object with two fields: &#39;isNewTopic&#39; (boolean) and &#39;title&#39; (string, or null if isNewTopic is false). Only include these fields, no other text.</span></span></code></pre>
<p>Like any other input, my <code>save that and execute</code> command had to be run through determining whether it’s a new topic or not.</p>
<p>Saving the file involved two tool usage calls:</p>
<ul>
<li><code>Replace</code> to write the file</li>
<li><code>Bash</code> with <code>chmod +x hn_top.sh &amp;&amp; hn_top.sh</code></li>
</ul>
<p>Now comes the fun part. Any <code>Bash</code> tool use is preceded by this this prompt:</p>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="plaintext"><code><span class="line"><span>&lt;policy_spec&gt;</span></span>
<span class="line"><span># Claude Code Code Bash command prefix detection</span></span>
<span class="line"><span></span></span>
<span class="line"><span>This document defines risk levels for actions that the Claude Code agent may take. This classification system is part of a broader safety framework and is used to determine when additional user confirmation or oversight may be needed.</span></span>
<span class="line"><span></span></span>
<span class="line"><span>## Definitions</span></span>
<span class="line"><span></span></span>
<span class="line"><span>**Command Injection:** Any technique used that would result in a command being run other than the detected prefix.</span></span>
<span class="line"><span></span></span>
<span class="line"><span>## Command prefix extraction examples</span></span>
<span class="line"><span>Examples:</span></span>
<span class="line"><span>- cat foo.txt =&gt; cat</span></span>
<span class="line"><span>- cd src =&gt; cd</span></span>
<span class="line"><span>- cd path/to/files/ =&gt; cd</span></span>
<span class="line"><span>- find ./src -type f -name \&#34;*.ts\&#34; =&gt; find</span></span>
<span class="line"><span>- gg cat foo.py =&gt; gg cat</span></span>
<span class="line"><span>- gg cp foo.py bar.py =&gt; gg cp</span></span>
<span class="line"><span>- git commit -m \&#34;foo\&#34; =&gt; git commit</span></span>
<span class="line"><span>- git diff HEAD~1 =&gt; git diff</span></span>
<span class="line"><span>- git diff --staged =&gt; git diff</span></span>
<span class="line"><span>- git diff $(pwd) =&gt; command_injection_detected</span></span>
<span class="line"><span>- git status =&gt; git status</span></span>
<span class="line"><span>- git status# test(`id`) =&gt; command_injection_detected</span></span>
<span class="line"><span>- git status`ls` =&gt; command_injection_detected</span></span>
<span class="line"><span>- git push =&gt; none</span></span>
<span class="line"><span>- git push origin master =&gt; git push</span></span>
<span class="line"><span>- git log -n 5 =&gt; git log</span></span>
<span class="line"><span>- git log --oneline -n 5 =&gt; git log</span></span>
<span class="line"><span>- grep -A 40 \&#34;from foo.bar.baz import\&#34; alpha/beta/gamma.py =&gt; grep</span></span>
<span class="line"><span>- pig tail zerba.log =&gt; pig tail</span></span>
<span class="line"><span>- potion test some/specific/file.ts =&gt; potion test</span></span>
<span class="line"><span>- npm run lint =&gt; none</span></span>
<span class="line"><span>- npm run lint -- \&#34;foo\&#34; =&gt; npm run lint</span></span>
<span class="line"><span>- npm test =&gt; none</span></span>
<span class="line"><span>- npm test --foo =&gt; npm test</span></span>
<span class="line"><span>- npm test -- -f \&#34;foo\&#34; =&gt; npm test</span></span>
<span class="line"><span>- pwd</span></span>
<span class="line"><span> curl example.com =&gt; command_injection_detected</span></span>
<span class="line"><span>- pytest foo/bar.py =&gt; pytest</span></span>
<span class="line"><span>- scalac build =&gt; none</span></span>
<span class="line"><span>- sleep 3 =&gt; sleep</span></span>
<span class="line"><span>&lt;/policy_spec&gt;</span></span>
<span class="line"><span></span></span>
<span class="line"><span>The user has allowed certain command prefixes to be run, and will otherwise be asked to approve or deny the command.</span></span>
<span class="line"><span>Your task is to determine the command prefix for the following command.</span></span>
<span class="line"><span></span></span>
<span class="line"><span>IMPORTANT: Bash commands may run multiple commands that are chained together.</span></span>
<span class="line"><span>For safety, if the command seems to contain command injection, you must return \&#34;command_injection_detected\&#34;.</span></span>
<span class="line"><span>(This will help protect the user: if they think that they&#39;re allowlisting command A,</span></span>
<span class="line"><span>but the AI coding agent sends a malicious command that technically has the same prefix as command A,</span></span>
<span class="line"><span>then the safety system will see that you said “command_injection_detected” and ask the user for manual confirmation.)</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Note that not every command has a prefix. If a command has no prefix, return \&#34;none\&#34;.</span></span>
<span class="line"><span></span></span>
<span class="line"><span>ONLY return the prefix. Do not return any other text, markdown markers, or other content or formatting.</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Command: chmod +x hn_top.sh</span></span></code></pre>
<p>Once the security is assessed, it will try to grab the actual commands to let users make them always allowed to run</p>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="plaintext"><code><span class="line"><span>Extract any file paths that this command reads or modifies. For commands like &#34;git diff&#34; and &#34;cat&#34;, include the paths of files being shown. Use paths verbatim -- don&#39;t add any slashes or try to resolve them. Do not try to infer paths that were not explicitly listed in the command output.</span></span>
<span class="line"><span>Format your response as:</span></span>
<span class="line"><span>&lt;filepaths&gt;</span></span>
<span class="line"><span>path/to/file1</span></span>
<span class="line"><span>path/to/file2</span></span>
<span class="line"><span>&lt;/filepaths&gt;</span></span>
<span class="line"><span></span></span>
<span class="line"><span>If no files are read or modified, return empty filepaths tags: &lt;filepaths&gt;&lt;/filepaths&gt;</span></span>
<span class="line"><span>Do not include any other text in your response.</span></span></code></pre>
<p>Which renders the following UX bit:</p>
<img width="896" height="226" src="https://kirshatrov.com/assets/post-images/2025/claude-bash.png"/>
<p>You can imagine that having to call an LLM at most twice for every <code>Bash</code> tool use makes things somewhat slower.</p>
<h2 id="init">/init</h2>
<p>Claude Code provides <code>/init</code> command which creates <code>CLAUDE.md</code> with generated contents about your project. Below is the prompt for that.</p>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="plaintext"><code><span class="line"><span>Please analyze this codebase and create a CLAUDE.md file containing:</span></span>
<span class="line"><span>1. Build/lint/test commands - especially for running a single test</span></span>
<span class="line"><span>2. Code style guidelines including imports, formatting, types, naming conventions, error handling, etc.</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Usage notes:</span></span>
<span class="line"><span>- The file you create will be given to agentic coding agents (such as yourself) that operate in this repository. Make it about 20 lines long.\</span></span>
<span class="line"><span>- If there&#39;s already a CLAUDE.md, improve it.\</span></span>
<span class="line"><span>- If there are Cursor rules (in .cursor/rules/ or .cursorrules) or Copilot rules (in .github/copilot-instructions.md), make sure to include them.\</span></span>
<span class="line"><span>- Be sure to prefix the file with the following text:</span></span>
<span class="line"><span></span></span>
<span class="line"><span># CLAUDE.md</span></span>
<span class="line"><span></span></span>
<span class="line"><span>This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.</span></span></code></pre>
<p>The prompt launches a <code>BatchTool</code> agent with a few more tool calls to collect more files — based on that context prompt that hints it the directory tree:</p>
<pre class="astro-code github-light" style="background-color:#fff;color:#24292e; overflow-x: auto;" tabindex="0" data-language="json"><code><span class="line"><span style="color:#24292E">{</span></span>
<span class="line"><span style="color:#005CC5">  &#34;type&#34;</span><span style="color:#24292E">: </span><span style="color:#032F62">&#34;tool_use&#34;</span><span style="color:#24292E">,</span></span>
<span class="line"><span style="color:#005CC5">  &#34;id&#34;</span><span style="color:#24292E">: </span><span style="color:#032F62">&#34;toolu_01GmZ4d81pGHKaqryDEuZQNm&#34;</span><span style="color:#24292E">,</span></span>
<span class="line"><span style="color:#005CC5">  &#34;name&#34;</span><span style="color:#24292E">: </span><span style="color:#032F62">&#34;BatchTool&#34;</span><span style="color:#24292E">,</span></span>
<span class="line"><span style="color:#005CC5">  &#34;input&#34;</span><span style="color:#24292E">: {</span></span>
<span class="line"><span style="color:#005CC5">    &#34;description&#34;</span><span style="color:#24292E">: </span><span style="color:#032F62">&#34;Gather repository information&#34;</span><span style="color:#24292E">,</span></span>
<span class="line"><span style="color:#005CC5">    &#34;invocations&#34;</span><span style="color:#24292E">: [</span></span>
<span class="line"><span style="color:#24292E">      {</span></span>
<span class="line"><span style="color:#005CC5">        &#34;tool_name&#34;</span><span style="color:#24292E">: </span><span style="color:#032F62">&#34;GlobTool&#34;</span><span style="color:#24292E">,</span></span>
<span class="line"><span style="color:#005CC5">        &#34;input&#34;</span><span style="color:#24292E">: {</span></span>
<span class="line"><span style="color:#005CC5">            &#34;pattern&#34;</span><span style="color:#24292E">: </span><span style="color:#032F62">&#34;package*.json&#34;</span></span>
<span class="line"><span style="color:#24292E">        }</span></span>
<span class="line"><span style="color:#24292E">      },</span></span>
<span class="line"><span style="color:#24292E">      {</span></span>
<span class="line"><span style="color:#005CC5">        &#34;tool_name&#34;</span><span style="color:#24292E">: </span><span style="color:#032F62">&#34;GlobTool&#34;</span><span style="color:#24292E">,</span></span>
<span class="line"><span style="color:#005CC5">        &#34;input&#34;</span><span style="color:#24292E">: {</span></span>
<span class="line"><span style="color:#005CC5">            &#34;pattern&#34;</span><span style="color:#24292E">: </span><span style="color:#032F62">&#34;*.md&#34;</span></span>
<span class="line"><span style="color:#24292E">        }</span></span>
<span class="line"><span style="color:#24292E">      },</span></span>
<span class="line"><span style="color:#24292E">      {</span></span>
<span class="line"><span style="color:#005CC5">        &#34;tool_name&#34;</span><span style="color:#24292E">: </span><span style="color:#032F62">&#34;GlobTool&#34;</span><span style="color:#24292E">,</span></span>
<span class="line"><span style="color:#005CC5">        &#34;input&#34;</span><span style="color:#24292E">: {</span></span>
<span class="line"><span style="color:#005CC5">            &#34;pattern&#34;</span><span style="color:#24292E">: </span><span style="color:#032F62">&#34;.cursor/rules/**&#34;</span></span>
<span class="line"><span style="color:#24292E">        }</span></span>
<span class="line"><span style="color:#24292E">      },</span></span>
<span class="line"><span style="color:#24292E">      {</span></span>
<span class="line"><span style="color:#005CC5">        &#34;tool_name&#34;</span><span style="color:#24292E">: </span><span style="color:#032F62">&#34;GlobTool&#34;</span><span style="color:#24292E">,</span></span>
<span class="line"><span style="color:#005CC5">        &#34;input&#34;</span><span style="color:#24292E">: {</span></span>
<span class="line"><span style="color:#005CC5">            &#34;pattern&#34;</span><span style="color:#24292E">: </span><span style="color:#032F62">&#34;.cursorrules/**&#34;</span></span>
<span class="line"><span style="color:#24292E">        }</span></span>
<span class="line"><span style="color:#24292E">      },</span></span>
<span class="line"><span style="color:#24292E">      {</span></span>
<span class="line"><span style="color:#005CC5">        &#34;tool_name&#34;</span><span style="color:#24292E">: </span><span style="color:#032F62">&#34;GlobTool&#34;</span><span style="color:#24292E">,</span></span>
<span class="line"><span style="color:#005CC5">        &#34;input&#34;</span><span style="color:#24292E">: {</span></span>
<span class="line"><span style="color:#005CC5">            &#34;pattern&#34;</span><span style="color:#24292E">: </span><span style="color:#032F62">&#34;.github/copilot-instructions.md&#34;</span></span>
<span class="line"><span style="color:#24292E">        }</span></span>
<span class="line"><span style="color:#24292E">      }</span></span>
<span class="line"><span style="color:#24292E">    ]</span></span>
<span class="line"><span style="color:#24292E">  }</span></span>
<span class="line"><span style="color:#24292E">}</span></span></code></pre>
<h2 id="wrap-up">Wrap up</h2>
<p>Overall, Claude Code tends to be more generic and secure for the price of extra time and money billed on more tokens.</p>
<p>This is very much unlike Cursor which would never attempt to analyze suggested bash tools. And I would have most often prefer that Claude Code’s approach.</p>
<p>I’ve also noticed that it uses <code>claude-3-7-sonnet</code> for most things that require reasoning, and <code>claude-3-5-haiku</code> for simpler calls like parsing Bash commands. But still, <code>claude-3.5</code> can be quite an overkill for those and <code>gpt-3.5-turbo</code> would do that 10x cheaper ($1.5 vs $15 per 1M output tokens).</p>
<p>Still, in terms of UX, Claude Code is the winner for an agentic tool that runs in console. <a href="https://github.com/Aider-AI/aider" rel="noreferrer" target="_blank">Aider</a> is close, but not nearly there in terms of reliability and UX. While Claude Code is a single NPM package (still requires a modern-ish version of Node which likely requires NVM installed), Aider’s installation process self-installed <code>uv</code> on my machine just because it needed it. And <a href="https://github.com/All-Hands-AI/OpenHands" rel="noreferrer" target="_blank">OpenHands</a> had to download ~10Gb of Docker images.</p>]]></content:encoded>
    </item>
  </channel>
</rss>